This commit is contained in:
2025-10-17 14:37:46 +08:00
parent f7a356c157
commit c06a3a9479
12 changed files with 3194 additions and 6293 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ lerna-debug.log*
node_modules
dist
dist-iife
dist-ssr
*.local

View File

@@ -1,5 +1,5 @@
# data-fetch-plugin
# kintone-data-aggregator-plugin
1. コマンド:
- `package.json`ファイルを開き、`scripts`内の`upload`コマンドのパラメータを接続する必要があるkintoneドメインに変更してください。

1
components.d.ts vendored
View File

@@ -2,6 +2,7 @@
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {}
/* prettier-ignore */

4327
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,11 @@
{
"name": "data-fetch-plugin",
"name": "kintone-data-aggregator-plugin",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"preview": "vite preview",
"build": "vite build && npm run pkg",
"vite:build": "vite build",
"build": "vite build && npm run pkg",
"build-upload": "npm run build && npm run upload",
"build-up2":"npm run build && npm run upload2",
"pkg": "kintone-plugin-packer --ppk private.ppk --out dist/plugin.zip dist/src",
@@ -15,7 +13,7 @@
"upload2": "kintone-plugin-uploader --base-url https://alicorn.cybozu.com --username maxz --password 7ld7i8vd dist/plugin.zip "
},
"dependencies": {
"@kintone/rest-api-client": "^5.7.0",
"@kintone/rest-api-client": "^5.7.5",
"kintone-ui-component": "1.18.0",
"rollup-plugin-css-only": "^4.5.2",
"vue": "^3.5.13",
@@ -26,9 +24,11 @@
"@kintone/dts-gen": "^8.1.1",
"@kintone/plugin-packer": "^8.1.3",
"@kintone/plugin-uploader": "^9.1.2",
"@types/node-rsa": "^1.1.4",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"eslint": "^8.57.0",
"node-rsa": "^1.1.1",
"rollup-plugin-copy": "^3.5.0",
"typescript": "^5.7.3",
"unplugin-vue-components": "^28.0.0",

View File

@@ -1,15 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCF7z/zsYmoe+L1AYTeCYvy9yBXlsXOniTzox6svsXunibVP3y+
f1jEwu2cnTdp/GABOzsVHNSrYGedRDlwG93Y8qxe7qNKLZAFL6ujmJ0FJixuYrh4
xvaWR6SlKIbws+803qAyE6dUN893xeZeJdWGelZNBsCZu8Nwmi28k1flzQIDAQAB
AoGAbWJchJZ2qtejIB5BeWWqmqAiFebZXkniO+j44HReCue3J2pWYu52fRwGG2Z7
H2AyuE67jh6hweVWOibCEkFwCM+MwkSpKNRyFqJwdzZGoMm/oT67dDGYELrmNCx/
9G5DdLgLXsA2dAANxTybaK8wg123Hhrh7NwJDETn9OC+uzECQQDeJTq4OSK9qUw9
RCpgijpVdnzc4hC0CNjKe/+z8bQOPVcX7zLcggwX/7i2UmNxBxfYFrCN8XIGJNGN
VXMpUdCjAkEAmliRAdgAJvoMvaS+gCcJt9tU18F2aunnGudpdwMWDFYdsnztLSJQ
uLPsPQM0TJJYwXWZ+akQuReqXeKg4WgmzwJBAMZAg38VvqN1C81BoHA37IeJDzYx
qqaBnrhWoYV+GCr9I1UA7GtOxGxGlBpivMyKgAUher+y0wgYo8t2jyg5E/ECQCRH
JO42AvMmWtBIZK5ifppEZ1C/HEJM8BEWy2c5xnjn1NsbGfQ92JNRVvmQQz6sN0hh
h+tynYej1Ft05TOV82kCQQCDd0/JtINW3Myj2nWIe8c9IjsBUtNOkaCa13tGOzwJ
3G8Bg0GzdVSC73OnEaguC72kBvyGO4enUFkOq6p6kmFQ
MIICXAIBAAKBgQCIkxa8JuBndXN0U/Fo+iX97nm03VYx0L4BBOfiUEB0innnLntF
Nm6IN7gaR+D1G4CGV6NkM/s7xRUPDFLRFQM0DUTw/Su40jcFdHNLbTntlrzh6lGb
ihPRficSP4Bu0nacTrLwEYwSp17Z95Ly2EliznuWh7ShaABaP6dfiy6SWwIDAQAB
AoGAA9N7E5JTPxK/RUcEwFghsFd5+WMqAwNCD20/wctVwu4ONDfNJEc+GXrWIYZZ
+wPmOd1Cqo/6PdZrnJxPxaO9DzFQelK4a+/5fXzOJm04d22Y8ezDJi9lsZ+wRG/8
ViEchiTB2ZZsRy8MPCVNyW2i1XOKPVZHAbH8hRDx3atbFmECQQD7G9NeNf32cwke
pDTa5bFoOQ9BOL44c1buf1eTIvY/aPBJG426ZXVEbCtknLBBiWj6+ESNKd7Zu8RE
cNgALnbNAkEAizwhFEJTn1TK7zyC3BqysPkDgMpe6naBnnNI3DjsYZpTKdhP5auw
cYe1nCyJgGdsY3OU1a8EOttOYxm4dAcdxwJAa483spsd9XrA5Ucg/MJ2g1Ytmppb
oIKHMhGJyZSoNfEVO7t5seXTnUNYMviK5wec2COP+Yd91XYAU/CiFTHR4QJAH3xF
brNg/ZLLITZp0SQobGSjKiiZNiP+Q7qGK2aj7Z5cWAYWazVb/RBXPS3FGkhI9mRY
r8mYB0c4fqh+UZ6aWQJBAMkM+Zs5Htt8xWnX4MLcGUlBLuUq2012NtMDWqH1GPd9
R5cyDpj2YCr3Wm6cASE7W2VXq6no8rMuoFm5vp4kR3c=
-----END RSA PRIVATE KEY-----

12
scripts/privateKey.ts Normal file
View File

@@ -0,0 +1,12 @@
'use strict';
// @ts-ignore
import RSA from 'node-rsa';
/**
* Create a private key for a kintone plugin
* Copy from https://github.com/kintone/js-sdk/blob/main/packages/create-plugin/src/privateKey.ts
*/
export const generatePrivateKey = () => {
const key = new RSA({ b: 1024 });
return key.exportKey('pkcs1-private');
};

View File

@@ -1,5 +1,5 @@
import { createApp } from 'vue';
import Config from './components/Config.vue';
import i18n from './i18n/index';
import i18n from './i18n';
createApp(Config, { pluginId: kintone.$PLUGIN_ID }).use(i18n).mount('#app');

View File

@@ -6,6 +6,12 @@
"./node_modules/@kintone/dts-gen/kintone.d.ts",
],
"compilerOptions": {
"resolveJsonModule": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]

View File

@@ -1,10 +1,32 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import copy from "rollup-plugin-copy";
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import copy from 'rollup-plugin-copy';
import * as path from 'path';
import * as fs from 'fs';
import * as child_process from 'child_process';
import Components from 'unplugin-vue-components/vite';
import { generatePrivateKey } from './scripts/privateKey';
const packageJson = require('./package.json');
// 从 package.json 读取 kintone-ui-component 版本信息
const kucVersion = packageJson.dependencies['kintone-ui-component'];
const formattedVersion = kucVersion.replace(/\./g, '-'); // 例如1.22.0 -> 1-22-0
// 判断是否为生产环境构建
const isProd = process.env.BUILD_MODE === 'production';
const buildMode = isProd ? 'production' : 'development';
console.log(`Building IIFE versions in ${buildMode} mode...`);
// 检查 private.ppk 是否存在,如果不存在则生成
const privateKeyPath = path.resolve(__dirname, 'private.ppk');
if (!fs.existsSync(privateKeyPath)) {
const privateKey = generatePrivateKey();
fs.writeFileSync(privateKeyPath, privateKey);
}
/**
* 自定义插件 vite-plugin-replace-tags替换 KUC 组件标签为包含版本号的标签,并自动导入组件
*/
function replaceKucTagsPlugin() {
return {
name: 'vite-plugin-replace-tags',
@@ -12,24 +34,28 @@ function replaceKucTagsPlugin() {
if (id.endsWith('.vue')) {
const content = await fs.promises.readFile(id, 'utf-8');
const usedComponent = {}
const usedComponent = {};
// 替换 <kuc-*> 为带有版本号的形式 <kuc-[version]-*>
let res = content
.replace(/<\/kuc-([a-zA-Z0-9-]+)(?![0-9-])>/g, (match, p1) => `</kuc-${p1}-1-18-0>`)
.replace(/<kuc-([a-zA-Z0-9-]+)(?![0-9-])([^>]*)>/g, (match, p1, p2) => {
.replace(new RegExp(`</kuc-([a-zA-Z0-9-]+)(?![0-9-])>`, 'g'), (match, p1) => `</kuc-${p1}-${formattedVersion}>`)
.replace(new RegExp(`<kuc-([a-zA-Z0-9-]+)(?![0-9-])([^>]*)>`, 'g'), (match, p1, p2) => {
usedComponent[p1] = true;
return `<kuc-${p1}-1-18-0${p2}>`
return `<kuc-${p1}-${formattedVersion}${p2}>`;
});
// 如果有 KUC 组件,自动生成 import 脚本
if (Object.keys(usedComponent).length) {
let importScript = '<script lang="ts">'
let importScript = '<script lang="ts">';
Object.keys(usedComponent).forEach((key) => {
const keyPascal = key.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
// 特殊处理 multi-choice 组件名称
if (key === 'multi-choice') {
key = 'multichoice';
}
importScript += `import * as Kuc${keyPascal} from "kintone-ui-component/lib/${key}";`
importScript += `import * as Kuc${keyPascal} from "kintone-ui-component/lib/${key}";`;
});
importScript += '</script>';
res = importScript + res;
@@ -37,52 +63,121 @@ function replaceKucTagsPlugin() {
return res;
}
}
},
};
}
/**
* 自定义插件 iife-builder构建 desktop.ts 和 mobile.ts 的 IIFE 版本
*/
function buildIIFEVersions() {
return {
name: 'iife-builder',
buildStart() {
console.log('Building IIFE versions...');
try {
// Build desktop IIFE
console.log('Building desktop IIFE...');
child_process.execSync('npx vite build --config vite.iife.config.ts', { stdio: 'inherit', env: { ...process.env, PLATFORM: 'desktop' } });
// Build mobile IIFE
console.log('Building mobile IIFE...');
child_process.execSync('npx vite build --config vite.iife.config.ts', { stdio: 'inherit', env: { ...process.env, PLATFORM: 'mobile' } });
console.log('IIFE builds completed successfully.');
} catch (error) {
console.error('Error building IIFE versions:', error);
throw error;
}
},
generateBundle(options, bundle) {
// 将 IIFE 版本复制到主构建目录覆盖原有文件
const desktopIIFEPath = path.resolve(__dirname, 'dist-iife/desktop/desktop.js');
const mobileIIFEPath = path.resolve(__dirname, 'dist-iife/mobile/mobile.js');
const destDir = path.resolve(__dirname, 'dist/src/js');
if (fs.existsSync(desktopIIFEPath)) {
fs.mkdirSync(destDir, { recursive: true });
fs.copyFileSync(desktopIIFEPath, path.join(destDir, 'desktop.js'));
console.log('Copied desktop.js to dist/src/js/');
}
if (fs.existsSync(mobileIIFEPath)) {
fs.mkdirSync(destDir, { recursive: true });
fs.copyFileSync(mobileIIFEPath, path.join(destDir, 'mobile.js'));
console.log('Copied mobile.js to dist/src/js/');
}
// 清理临时的 dist-iife 目录
if (fs.existsSync(path.resolve(__dirname, 'dist-iife'))) {
fs.rmSync(path.resolve(__dirname, 'dist-iife'), { recursive: true, force: true });
console.log('Cleaned up dist-iife directory');
}
},
};
}
// https://vite.dev/config/
export default defineConfig({
plugins: [
buildIIFEVersions(),
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith("kuc-"),
}
}
// 将以 kuc- 开头的标签识别为自定义元素,避免 Vue 的警告
isCustomElement: (tag) => tag.startsWith('kuc-'),
},
},
}),
Components(),
Components(), // 自动导入 Vue 组件
copy({
// 将打包 plugin 需要的文件复制到 dist 里面
targets: [
{ src: 'dist/index.html', dest: 'dist/src/html', rename: 'config.html' },
{ src: 'src/manifest.json', dest: 'dist/src' },
{ src: 'src/assets/*.js', dest: 'dist/src/js' },
{ src: 'src/assets/*.png', dest: 'dist/src/image' },
{ src: 'src/css/*', dest: 'dist/src/css' },
// { src: 'node_modules/kintone-ui-component/umd/kuc.min.js', dest: 'dist/src/js' },
],
hook: 'writeBundle' // 指定在何时复制文件
}),
replaceKucTagsPlugin()
replaceKucTagsPlugin(), // 自定义标签替换插件
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@': path.resolve(__dirname, 'src'), // 配置 @ 别名指向 src 目录
},
},
build: {
rollupOptions: {
input: {
config: path.resolve(__dirname, 'index.html'),
desktop: path.resolve(__dirname, 'src/js/desktop.ts'),
mobile: path.resolve(__dirname, 'src/js/mobile.ts'),
},
output: {
entryFileNames: (chunkInfo) => {
return 'src/js/[name].js'; // 默认处理为 JS 文件
return 'src/js/[name].js'; // 将入口文件输出为 JS 文件
},
assetFileNames: 'src/[ext]/[name].[ext]',
assetFileNames: 'src/[ext]/[name].[ext]', // 设置资源文件输出路径
},
},
sourcemap: 'inline',
}
})
sourcemap: isProd ? false : 'inline', // 生产环境关闭 sourcemap开发环境使用 inline
minify: isProd ? 'terser' : false, // 生产环境使用 terser 混淆,开发环境不混淆
...(isProd && {
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true, // 移除 debugger
pure_funcs: ['console.log'], // 移除 console.log
},
mangle: {
toplevel: true, // 混淆顶级作用域的变量名
},
format: {
comments: false, // 移除注释
},
},
}),
},
});

72
vite.iife.config.ts Normal file
View File

@@ -0,0 +1,72 @@
import * as path from 'path';
import { defineConfig, UserConfig } from 'vite';
const packageJson = require('./package.json');
/**
* 生成 IIFE 配置的工厂函数
* @param platform 'desktop' | 'mobile'
*/
function createIIFEConfig(platform: 'desktop' | 'mobile'): UserConfig {
// 判断是否为生产环境构建
const isProd = process.env.BUILD_MODE === 'production';
return {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env': JSON.stringify({}),
global: 'window',
},
build: {
lib: {
entry: path.resolve(__dirname, `src/js/${platform}.ts`),
name: `${packageJson.name.replaceAll('-', '_')}_${platform}_iife`,
formats: ['iife'],
fileName: () => `${platform}.js`,
},
rollupOptions: {
external: ['kintone'], // kintone 是网站提供的 api需要处理
output: {
globals: {
kintone: 'kintone',
},
},
},
outDir: path.resolve(__dirname, `dist-iife/${platform}`), // 输出到 dist-iife 目录
sourcemap: isProd ? false : 'inline', // 生产环境关闭,开发环境使用 inline
minify: isProd ? 'terser' : false, // 生产环境使用 terser 混淆,开发环境不混淆
...(isProd && {
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true, // 移除 debugger
pure_funcs: ['console.log'], // 移除 console.log
},
mangle: {
toplevel: true, // 混淆顶级作用域的变量名
},
format: {
comments: false, // 移除注释
},
},
}),
},
};
}
// 根据环境变量决定构建哪个平台
const platform = process.env.PLATFORM as 'desktop' | 'mobile';
if (!platform || !['desktop', 'mobile'].includes(platform)) {
throw new Error('Please specify PLATFORM environment variable: desktop or mobile');
}
/**
* 统一的 IIFE 构建配置
* 用于将 desktop.ts 和 mobile.ts 打包为 IIFE 格式
*/
export default defineConfig(createIIFEConfig(platform));

4885
yarn.lock

File diff suppressed because it is too large Load Diff