Compare commits

...

3 Commits

Author SHA1 Message Date
hsueh chiahao
67afc7be3c fix 2025-10-16 21:27:27 +08:00
hsueh chiahao
ee7acb6486 fix 2025-10-16 15:49:27 +08:00
hsueh chiahao
4cba63fc16 update 2025-10-16 15:40:55 +08:00
22 changed files with 3342 additions and 8745 deletions

1
.gitignore vendored
View File

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

View File

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

1
components.d.ts vendored
View File

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

4414
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because one or more lines are too long

2377
src/assets/kuc.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,9 @@
import type { FieldLayout, FieldsJoinMapping, JoinTable, Record, RecordForParameter, SavedData,StringValue, WhereCondition } from "@/types/model"; import type { FieldLayout, FieldsJoinMapping, JoinTable, Record, RecordForParameter, SavedData,StringValue, WhereCondition } from "@/types/model";
import { type OneOf, isType } from "./field-types-mobile"; import { type OneOf, isType } from "./field-types-mobile";
import type { ConditionValue } from "./conditions"; import type { ConditionValue } from "./conditions";
declare var KintoneRestAPIClient: typeof import("@kintone/rest-api-client").KintoneRestAPIClient; import { KintoneRestAPIClient } from '@kintone/rest-api-client';
import { MobileButton } from 'kintone-ui-component/lib/mobile/button';
import { Spinner } from 'kintone-ui-component/lib/spinner';
export class KintoneIndexEventHandler { export class KintoneIndexEventHandler {
private config: SavedData<FieldLayout>; private config: SavedData<FieldLayout>;
private currentApp: string; private currentApp: string;
@@ -22,12 +24,11 @@ export class KintoneIndexEventHandler {
}; };
// ボタン追加 // ボタン追加
if (document.getElementById('btn-data-fetch')) return; if (document.getElementById('aggregator-plugin:btn-data-fetch')) return;
const kuc = Kucs['1.18.0']; const button = new MobileButton({
const button = new kuc.MobileButton({
text: this.config.buttonName, text: this.config.buttonName,
type: "submit", type: "submit",
id: 'btn-data-fetch', id: 'aggregator-plugin:btn-data-fetch',
}); });
button.addEventListener('click', () => this.handleButtonClick()); button.addEventListener('click', () => this.handleButtonClick());
headerSpace.appendChild(button); headerSpace.appendChild(button);
@@ -51,8 +52,7 @@ export class KintoneIndexEventHandler {
} }
private showSpinner() { private showSpinner() {
const kuc = Kucs['1.18.0']; const spinner = new Spinner({
const spinner = new kuc.Spinner({
text: 'データ収集中', text: 'データ収集中',
container: document.body container: document.body
}); });

View File

@@ -1,7 +1,9 @@
import type { FieldLayout, FieldsJoinMapping, JoinTable, Record, RecordForParameter, SavedData, StringValue,WhereCondition } from "@/types/model"; import type { FieldLayout, FieldsJoinMapping, JoinTable, Record, RecordForParameter, SavedData, StringValue,WhereCondition } from "@/types/model";
import { type OneOf, isType } from "./field-types"; import { type OneOf, isType } from "./field-types";
import type { ConditionValue } from "./conditions"; import type { ConditionValue } from "./conditions";
declare var KintoneRestAPIClient: typeof import("@kintone/rest-api-client").KintoneRestAPIClient; import { KintoneRestAPIClient } from '@kintone/rest-api-client';
import { Button } from 'kintone-ui-component/lib/button';
import { Spinner } from 'kintone-ui-component/lib/spinner';
export class KintoneIndexEventHandler { export class KintoneIndexEventHandler {
private config: SavedData<FieldLayout>; private config: SavedData<FieldLayout>;
private currentApp: string; private currentApp: string;
@@ -22,12 +24,11 @@ export class KintoneIndexEventHandler {
}; };
// ボタン追加 // ボタン追加
if (document.getElementById('btn-data-fetch')) return; if (document.getElementById('aggregator-plugin:btn-data-fetch')) return;
const kuc = Kucs['1.18.0']; const button = new Button({
const button = new kuc.Button({
text: this.config.buttonName, text: this.config.buttonName,
type: "submit", type: "submit",
id: 'btn-data-fetch', id: 'aggregator-plugin:btn-data-fetch',
}); });
button.addEventListener('click', () => this.handleButtonClick()); button.addEventListener('click', () => this.handleButtonClick());
headerSpace.appendChild(button); headerSpace.appendChild(button);
@@ -51,8 +52,7 @@ export class KintoneIndexEventHandler {
} }
private showSpinner() { private showSpinner() {
const kuc = Kucs['1.18.0']; const spinner = new Spinner({
const spinner = new kuc.Spinner({
text: 'データ収集中', text: 'データ収集中',
container: document.body container: document.body
}); });

View File

@@ -1,34 +1,32 @@
import type { Field, FieldLayout, SavedData } from "@/types/model"; import type { FieldLayout, SavedData } from '@/types/model';
import { KintoneIndexEventHandler } from "./KintoneIndexEventHandler"; import { KintoneIndexEventHandler } from './KintoneIndexEventHandler';
(function (PLUGIN_ID) { (function (PLUGIN_ID) {
kintone.events.on('app.record.index.show', (event) => { kintone.events.on('app.record.index.show', (event) => {
try{ try {
const setting = kintone.plugin.app.getConfig(PLUGIN_ID); const setting = kintone.plugin.app.getConfig(PLUGIN_ID);
const config:SavedData<FieldLayout> = getConfig(setting); const config: SavedData<FieldLayout> = getConfig(setting);
const currentApp = kintone.app.getId()?.toString(); const currentApp = kintone.app.getId()?.toString();
if(!currentApp) return; if (!currentApp) return;
const handler = new KintoneIndexEventHandler(config,currentApp); const handler = new KintoneIndexEventHandler(config, currentApp);
handler.init(); handler.init();
}catch(error){ } catch (error) {
const detailError =(error instanceof Error) ? "\n詳細:" + error.message : ""; const detailError = error instanceof Error ? '\n詳細:' + error.message : '';
const errorMsg = `データ収集中処理中例外発生しました。${ detailError }`; const errorMsg = `データ収集中処理中例外発生しました。${detailError}`;
event.error = errorMsg; event.error = errorMsg;
} }
return event; return event;
}); });
/** /**
* Config設定値を変換する * Config設定値を変換する
* @param setting * @param setting
* @returns * @returns
*/ */
function getConfig(setting:any):SavedData<FieldLayout>{ function getConfig(setting: any): SavedData<FieldLayout> {
const config:SavedData<FieldLayout>={ const config: SavedData<FieldLayout> = {
buttonName:setting.buttonName, buttonName: setting.buttonName,
joinTables:JSON.parse(setting.joinTables) joinTables: JSON.parse(setting.joinTables),
} };
return config; return config;
} }
})(kintone.$PLUGIN_ID); })(kintone.$PLUGIN_ID);

View File

@@ -1,4 +1,4 @@
declare var KintoneRestAPIClient: typeof import("@kintone/rest-api-client").KintoneRestAPIClient; import { KintoneRestAPIClient } from '@kintone/rest-api-client';
const client = new KintoneRestAPIClient(); const client = new KintoneRestAPIClient();
export type Properties = Awaited<ReturnType<typeof client.app.getFormFields>>['properties']; export type Properties = Awaited<ReturnType<typeof client.app.getFormFields>>['properties'];
export type Layout = Awaited<ReturnType<typeof client.app.getFormLayout>>['layout']; export type Layout = Awaited<ReturnType<typeof client.app.getFormLayout>>['layout'];

View File

@@ -1,4 +1,4 @@
declare var KintoneRestAPIClient: typeof import("@kintone/rest-api-client").KintoneRestAPIClient; import { KintoneRestAPIClient } from '@kintone/rest-api-client';
const client = new KintoneRestAPIClient(); const client = new KintoneRestAPIClient();
export type Properties = Awaited<ReturnType<typeof client.app.getFormFields>>['properties']; export type Properties = Awaited<ReturnType<typeof client.app.getFormFields>>['properties'];
export type Layout = Awaited<ReturnType<typeof client.app.getFormLayout>>['layout']; export type Layout = Awaited<ReturnType<typeof client.app.getFormLayout>>['layout'];

View File

@@ -1,32 +1,32 @@
import type { Field, FieldLayout, SavedData } from "@/types/model"; import type { FieldLayout, SavedData } from '@/types/model';
import { KintoneIndexEventHandler } from "./KintoneIndexEventHandler.mobile"; import { KintoneIndexEventHandler } from './KintoneIndexEventHandler.mobile';
(function (PLUGIN_ID) { (function (PLUGIN_ID) {
kintone.events.on('mobile.app.record.index.show', (event) => { kintone.events.on('mobile.app.record.index.show', (event) => {
try{ try {
const setting = kintone.plugin.app.getConfig(PLUGIN_ID); const setting = kintone.plugin.app.getConfig(PLUGIN_ID);
const config:SavedData<FieldLayout> = getConfig(setting); const config: SavedData<FieldLayout> = getConfig(setting);
const currentApp = kintone.mobile.app.getId()?.toString(); const currentApp = kintone.mobile.app.getId()?.toString();
if(!currentApp) return; if (!currentApp) return;
const handler = new KintoneIndexEventHandler(config,currentApp); const handler = new KintoneIndexEventHandler(config, currentApp);
handler.init(); handler.init();
}catch(error){ } catch (error) {
const detailError =(error instanceof Error) ? "\n詳細:" + error.message : ""; const detailError = error instanceof Error ? '\n詳細:' + error.message : '';
const errorMsg = `データ収集中処理中例外発生しました。${ detailError }`; const errorMsg = `データ収集中処理中例外発生しました。${detailError}`;
event.error = errorMsg; event.error = errorMsg;
} }
return event; return event;
}); });
/** /**
* Config設定値を変換する * Config設定値を変換する
* @param setting * @param setting
* @returns * @returns
*/ */
function getConfig(setting:any):SavedData<FieldLayout>{ function getConfig(setting: any): SavedData<FieldLayout> {
const config:SavedData<FieldLayout>={ const config: SavedData<FieldLayout> = {
buttonName:setting.buttonName, buttonName: setting.buttonName,
joinTables:JSON.parse(setting.joinTables) joinTables: JSON.parse(setting.joinTables),
} };
return config; return config;
} }
})(kintone.$PLUGIN_ID); })(kintone.$PLUGIN_ID);

View File

@@ -1,5 +1,5 @@
import { createApp } from 'vue'; import { createApp } from 'vue';
import Config from './components/Config.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'); createApp(Config, { pluginId: kintone.$PLUGIN_ID }).use(i18n).mount('#app');

View File

@@ -5,12 +5,9 @@
"type": "APP", "type": "APP",
"desktop": { "desktop": {
"js": [ "js": [
"js/KintoneRestAPIClient.min.js",
"js/kuc.min.js",
"js/desktop.js" "js/desktop.js"
], ],
"css": [ "css": [
"css/51-modern-default.css",
"css/desktop.css" "css/desktop.css"
] ]
}, },
@@ -38,8 +35,6 @@
}, },
"mobile": { "mobile": {
"js": [ "js": [
"js/KintoneRestAPIClient.min.js",
"js/kuc.min.js",
"js/mobile.js" "js/mobile.js"
], ],
"css": [ "css": [

View File

@@ -1,6 +1,3 @@
declare global { // 导出所有类型定义
const Kucs: { // 主要导出应用设置相关的类型接口
[version: string]: any; export * from './model';
};
}
export {};

View File

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

View File

@@ -1,10 +1,32 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue';
import copy from "rollup-plugin-copy"; import copy from 'rollup-plugin-copy';
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import * as child_process from 'child_process';
import Components from 'unplugin-vue-components/vite'; 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() { function replaceKucTagsPlugin() {
return { return {
name: 'vite-plugin-replace-tags', name: 'vite-plugin-replace-tags',
@@ -12,24 +34,28 @@ function replaceKucTagsPlugin() {
if (id.endsWith('.vue')) { if (id.endsWith('.vue')) {
const content = await fs.promises.readFile(id, 'utf-8'); const content = await fs.promises.readFile(id, 'utf-8');
const usedComponent = {} const usedComponent = {};
// 替换 <kuc-*> 为带有版本号的形式 <kuc-[version]-*>
let res = content let res = content
.replace(/<\/kuc-([a-zA-Z0-9-]+)(?![0-9-])>/g, (match, p1) => `</kuc-${p1}-1-18-0>`) .replace(new RegExp(`</kuc-([a-zA-Z0-9-]+)(?![0-9-])>`, 'g'), (match, p1) => `</kuc-${p1}-${formattedVersion}>`)
.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, p2) => {
usedComponent[p1] = true; usedComponent[p1] = true;
return `<kuc-${p1}-1-18-0${p2}>` return `<kuc-${p1}-${formattedVersion}${p2}>`;
}); });
// 如果有 KUC 组件,自动生成 import 脚本
if (Object.keys(usedComponent).length) { if (Object.keys(usedComponent).length) {
let importScript = '<script lang="ts">' let importScript = '<script lang="ts">';
Object.keys(usedComponent).forEach((key) => { Object.keys(usedComponent).forEach((key) => {
const keyPascal = key.split('-') const keyPascal = key.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1)) .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(''); .join('');
// 特殊处理 multi-choice 组件名称
if (key === 'multi-choice') { if (key === 'multi-choice') {
key = 'multichoice'; 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>'; importScript += '</script>';
res = importScript + res; res = importScript + res;
@@ -37,52 +63,122 @@ function replaceKucTagsPlugin() {
return res; 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/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
buildIIFEVersions(),
vue({ vue({
template: { template: {
compilerOptions: { compilerOptions: {
isCustomElement: (tag) => tag.startsWith("kuc-"), // 将以 kuc- 开头的标签识别为自定义元素,避免 Vue 的警告
} isCustomElement: (tag) => tag.startsWith('kuc-'),
} },
},
}), }),
Components(), Components(), // 自动导入 Vue 组件
copy({ copy({
// 将打包 plugin 需要的文件复制到 dist 里面
targets: [ targets: [
{ src: 'dist/index.html', dest: 'dist/src/html', rename: 'config.html' }, { src: 'dist/index.html', dest: 'dist/src/html', rename: 'config.html' },
{ src: 'src/manifest.json', dest: 'dist/src' }, { src: 'src/manifest.json', dest: 'dist/src' },
{ src: 'src/assets/*.js', dest: 'dist/src/js' }, { src: 'src/assets/*.js', dest: 'dist/src/js' },
{ src: 'src/assets/*.png', dest: 'dist/src/image' }, { src: 'src/assets/*.png', dest: 'dist/src/image' },
{ src: 'src/css/*', dest: 'dist/src/css' }, { src: 'src/css/*', dest: 'dist/src/css' },
// { src: 'node_modules/kintone-ui-component/umd/kuc.min.js', dest: 'dist/src/js' },
], ],
hook: 'writeBundle' // 指定在何时复制文件 hook: 'writeBundle' // 指定在何时复制文件
}), }),
replaceKucTagsPlugin() replaceKucTagsPlugin(), // 自定义标签替换插件
], ],
resolve: { resolve: {
alias: { alias: {
'@': path.resolve(__dirname, 'src'), '@': path.resolve(__dirname, 'src'), // 配置 @ 别名指向 src 目录
}, },
}, },
build: { build: {
target: 'es2022',
rollupOptions: { rollupOptions: {
input: { input: {
config: path.resolve(__dirname, 'index.html'), config: path.resolve(__dirname, 'index.html'),
desktop: path.resolve(__dirname, 'src/js/desktop.ts'),
mobile: path.resolve(__dirname, 'src/js/mobile.ts'),
}, },
output: { output: {
entryFileNames: (chunkInfo) => { 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: 'terser', // 强制进行混淆,避免全局变量被污染的问题
}) ...(isProd && {
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true, // 移除 debugger
pure_funcs: ['console.log'], // 移除 console.log
},
mangle: {
toplevel: true, // 混淆顶级作用域的变量名
},
format: {
comments: false, // 移除注释
},
},
}),
},
});

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

@@ -0,0 +1,73 @@
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: {
target: 'es2022',
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: '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