代码批量prettier格式化

pull/170/head
zhangdaiscott 2022-06-10 10:44:44 +08:00
parent 38db7196c7
commit 6995e5e280
741 changed files with 37384 additions and 40333 deletions

View File

@ -30,12 +30,7 @@ export function getThemeColors(color?: string) {
return [...lightColors, ...modeColors]; return [...lightColors, ...modeColors];
} }
export function generateColors({ export function generateColors({ color = primaryColor, mixLighten, mixDarken, tinycolor }: GenerateColorsParams) {
color = primaryColor,
mixLighten,
mixDarken,
tinycolor,
}: GenerateColorsParams) {
const arr = new Array(19).fill(0); const arr = new Array(19).fill(0);
const lightens = arr.map((_t, i) => { const lightens = arr.map((_t, i) => {
return mixLighten(color, i / 5); return mixLighten(color, i / 5);
@ -68,12 +63,5 @@ export function generateColors({
.toHexString(); .toHexString();
}) })
.filter((item) => item !== '#000000'); .filter((item) => item !== '#000000');
return [ return [...lightens, ...darkens, ...alphaColors, ...shortAlphaColors, ...tinycolorDarkens, ...tinycolorLightens].filter((item) => !item.includes('-'));
...lightens,
...darkens,
...alphaColors,
...shortAlphaColors,
...tinycolorDarkens,
...tinycolorLightens,
].filter((item) => !item.includes('-'));
} }

View File

@ -51,21 +51,14 @@ async function generateIcon() {
if (data) { if (data) {
const { prefix } = data; const { prefix } = data;
const isLocal = useType === 'local'; const isLocal = useType === 'local';
const icons = Object.keys(data.icons).map( const icons = Object.keys(data.icons).map((item) => `${isLocal ? prefix + ':' : ''}${item}`);
(item) => `${isLocal ? prefix + ':' : ''}${item}`
);
await fs.writeFileSync( await fs.writeFileSync(path.join(output, `icons.data.ts`), `export default ${isLocal ? JSON.stringify(icons) : JSON.stringify({ prefix, icons })}`);
path.join(output, `icons.data.ts`),
`export default ${isLocal ? JSON.stringify(icons) : JSON.stringify({ prefix, icons })}`
);
prefixSet.push(prefix); prefixSet.push(prefix);
} }
} }
fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite')); fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
console.log( console.log(`${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`);
`${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`
);
}); });
} }

View File

@ -3,7 +3,5 @@
* @param env * @param env
*/ */
export const getConfigFileName = (env: Record<string, any>) => { export const getConfigFileName = (env: Record<string, any>) => {
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__` return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '');
.toUpperCase()
.replace(/\s/g, '');
}; };

View File

@ -11,7 +11,7 @@ export const runBuild = async () => {
// Generate configuration file // Generate configuration file
if (!argvList.includes('disabled-config')) { if (!argvList.includes('disabled-config')) {
runBuildConfig(); runBuildConfig();
} }
console.log(`${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!'); console.log(`${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');

View File

@ -3,60 +3,60 @@ import path from 'path';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
export function isDevFn(mode: string): boolean { export function isDevFn(mode: string): boolean {
return mode === 'development'; return mode === 'development';
} }
export function isProdFn(mode: string): boolean { export function isProdFn(mode: string): boolean {
return mode === 'production'; return mode === 'production';
} }
/** /**
* Whether to generate package preview * Whether to generate package preview
*/ */
export function isReportMode(): boolean { export function isReportMode(): boolean {
return process.env.REPORT === 'true'; return process.env.REPORT === 'true';
} }
// Read all environment variable configuration files to process.env // Read all environment variable configuration files to process.env
export function wrapperEnv(envConf: Recordable): ViteEnv { export function wrapperEnv(envConf: Recordable): ViteEnv {
const ret: any = {}; const ret: any = {};
for (const envName of Object.keys(envConf)) { for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n'); let realName = envConf[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName; realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') { if (envName === 'VITE_PORT') {
realName = Number(realName); realName = Number(realName);
}
if (envName === 'VITE_PROXY' && realName) {
try {
realName = JSON.parse(realName.replace(/'/g, '"'));
} catch (error) {
realName = '';
}
}
ret[envName] = realName;
if (typeof realName === 'string') {
process.env[envName] = realName;
} else if (typeof realName === 'object') {
process.env[envName] = JSON.stringify(realName);
}
} }
return ret; if (envName === 'VITE_PROXY' && realName) {
try {
realName = JSON.parse(realName.replace(/'/g, '"'));
} catch (error) {
realName = '';
}
}
ret[envName] = realName;
if (typeof realName === 'string') {
process.env[envName] = realName;
} else if (typeof realName === 'object') {
process.env[envName] = JSON.stringify(realName);
}
}
return ret;
} }
/** /**
* *
*/ */
function getConfFiles() { function getConfFiles() {
const script = process.env.npm_lifecycle_script; const script = process.env.npm_lifecycle_script;
const reg = new RegExp('--mode ([a-z_\\d]+)'); const reg = new RegExp('--mode ([a-z_\\d]+)');
const result = reg.exec(script as string) as any; const result = reg.exec(script as string) as any;
if (result) { if (result) {
const mode = result[1] as string; const mode = result[1] as string;
return ['.env', `.env.${mode}`]; return ['.env', `.env.${mode}`];
} }
return ['.env', '.env.production']; return ['.env', '.env.production'];
} }
/** /**
@ -65,22 +65,22 @@ function getConfFiles() {
* @param confFiles ext * @param confFiles ext
*/ */
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = getConfFiles()) { export function getEnvConfig(match = 'VITE_GLOB_', confFiles = getConfFiles()) {
let envConfig = {}; let envConfig = {};
confFiles.forEach((item) => { confFiles.forEach((item) => {
try { try {
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item))); const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
envConfig = {...envConfig, ...env}; envConfig = { ...envConfig, ...env };
} catch (e) { } catch (e) {
console.error(`Error in parsing ${item}`, e); console.error(`Error in parsing ${item}`, e);
} }
}); });
const reg = new RegExp(`^(${match})`); const reg = new RegExp(`^(${match})`);
Object.keys(envConfig).forEach((key) => { Object.keys(envConfig).forEach((key) => {
if (!reg.test(key)) { if (!reg.test(key)) {
Reflect.deleteProperty(envConfig, key); Reflect.deleteProperty(envConfig, key);
} }
}); });
return envConfig; return envConfig;
} }
/** /**
@ -88,5 +88,5 @@ export function getEnvConfig(match = 'VITE_GLOB_', confFiles = getConfFiles()) {
* @param dir file path * @param dir file path
*/ */
export function getRootPath(...dir: string[]) { export function getRootPath(...dir: string[]) {
return path.resolve(process.cwd(), ...dir); return path.resolve(process.cwd(), ...dir);
} }

View File

@ -5,10 +5,7 @@
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import compressPlugin from 'vite-plugin-compression'; import compressPlugin from 'vite-plugin-compression';
export function configCompressPlugin( export function configCompressPlugin(compress: 'gzip' | 'brotli' | 'none', deleteOriginFile = false): Plugin | Plugin[] {
compress: 'gzip' | 'brotli' | 'none',
deleteOriginFile = false
): Plugin | Plugin[] {
const compressList = compress.split(','); const compressList = compress.split(',');
const plugins: Plugin[] = []; const plugins: Plugin[] = [];

View File

@ -25,7 +25,7 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
}, },
// Embed the generated app.config.js file // Embed the generated app.config.js file
tags: isBuild tags: isBuild
? [ ? [
{ {
tag: 'script', tag: 'script',
attrs: { attrs: {
@ -33,7 +33,7 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
}, },
}, },
] ]
: [], : [],
}, },
}); });
return htmlPlugin; return htmlPlugin;

View File

@ -15,74 +15,66 @@ import { configThemePlugin } from './theme';
import { configImageminPlugin } from './imagemin'; import { configImageminPlugin } from './imagemin';
import { configSvgIconsPlugin } from './svgSprite'; import { configSvgIconsPlugin } from './svgSprite';
import { configHmrPlugin } from './hmr'; import { configHmrPlugin } from './hmr';
import OptimizationPersist from 'vite-plugin-optimize-persist' import OptimizationPersist from 'vite-plugin-optimize-persist';
import PkgConfig from 'vite-plugin-package-config' import PkgConfig from 'vite-plugin-package-config';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) { export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const { const { VITE_USE_IMAGEMIN, VITE_USE_MOCK, VITE_LEGACY, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
VITE_USE_IMAGEMIN,
VITE_USE_MOCK,
VITE_LEGACY,
VITE_BUILD_COMPRESS,
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE,
} = viteEnv;
const vitePlugins: (Plugin | Plugin[])[] = [ const vitePlugins: (Plugin | Plugin[])[] = [
// have to // have to
vue(), vue(),
// have to // have to
vueJsx(), vueJsx(),
// support name // support name
vueSetupExtend(), vueSetupExtend(),
]; ];
// vite-plugin-windicss // vite-plugin-windicss
vitePlugins.push(windiCSS()); vitePlugins.push(windiCSS());
// TODO // TODO
!isBuild && vitePlugins.push(configHmrPlugin()); !isBuild && vitePlugins.push(configHmrPlugin());
// @vitejs/plugin-legacy // @vitejs/plugin-legacy
VITE_LEGACY && isBuild && vitePlugins.push(legacy()); VITE_LEGACY && isBuild && vitePlugins.push(legacy());
// vite-plugin-html // vite-plugin-html
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild)); vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
// vite-plugin-svg-icons // vite-plugin-svg-icons
vitePlugins.push(configSvgIconsPlugin(isBuild)); vitePlugins.push(configSvgIconsPlugin(isBuild));
// vite-plugin-mock // vite-plugin-mock
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild)); VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild));
// vite-plugin-purge-icons // vite-plugin-purge-icons
vitePlugins.push(purgeIcons()); vitePlugins.push(purgeIcons());
// vite-plugin-style-import // vite-plugin-style-import
vitePlugins.push(configStyleImportPlugin(isBuild)); vitePlugins.push(configStyleImportPlugin(isBuild));
// rollup-plugin-visualizer // rollup-plugin-visualizer
vitePlugins.push(configVisualizerConfig()); vitePlugins.push(configVisualizerConfig());
//vite-plugin-theme //vite-plugin-theme
vitePlugins.push(configThemePlugin(isBuild)); vitePlugins.push(configThemePlugin(isBuild));
// The following plugins only work in the production environment // The following plugins only work in the production environment
if (isBuild) { if (isBuild) {
//vite-plugin-imagemin //vite-plugin-imagemin
VITE_USE_IMAGEMIN && vitePlugins.push(configImageminPlugin()); VITE_USE_IMAGEMIN && vitePlugins.push(configImageminPlugin());
// rollup-plugin-gzip // rollup-plugin-gzip
vitePlugins.push( vitePlugins.push(configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE));
configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE),
);
// vite-plugin-pwa // vite-plugin-pwa
vitePlugins.push(configPwaConfig(viteEnv)); vitePlugins.push(configPwaConfig(viteEnv));
} }
//vite-plugin-theme【解决vite首次打开界面加载慢问题】 //vite-plugin-theme【解决vite首次打开界面加载慢问题】
vitePlugins.push(PkgConfig()); vitePlugins.push(PkgConfig());
vitePlugins.push(OptimizationPersist()); vitePlugins.push(OptimizationPersist());
return vitePlugins; return vitePlugins;
} }

View File

@ -4,86 +4,80 @@
*/ */
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import path from 'path'; import path from 'path';
import { import { viteThemePlugin, antdDarkThemePlugin, mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme';
viteThemePlugin,
antdDarkThemePlugin,
mixLighten,
mixDarken,
tinycolor,
} from 'vite-plugin-theme';
import { getThemeColors, generateColors } from '../../config/themeConfig'; import { getThemeColors, generateColors } from '../../config/themeConfig';
import { generateModifyVars } from '../../generate/generateModifyVars'; import { generateModifyVars } from '../../generate/generateModifyVars';
export function configThemePlugin(isBuild: boolean): Plugin[] { export function configThemePlugin(isBuild: boolean): Plugin[] {
const colors = generateColors({ const colors = generateColors({
mixDarken, mixDarken,
mixLighten, mixLighten,
tinycolor, tinycolor,
}); });
const plugin = [ const plugin = [
viteThemePlugin({ viteThemePlugin({
resolveSelector: (s) => { resolveSelector: (s) => {
s = s.trim(); s = s.trim();
switch (s) { switch (s) {
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon': case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
return '.ant-steps-item-icon > .ant-steps-icon'; return '.ant-steps-item-icon > .ant-steps-icon';
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)': case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)':
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover': case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover':
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active': case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active':
return s; return s;
case '.ant-steps-item-icon > .ant-steps-icon': case '.ant-steps-item-icon > .ant-steps-icon':
return s; return s;
case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)': case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)':
return s; return s;
default: default:
if (s.indexOf('.ant-btn') >= -1) { if (s.indexOf('.ant-btn') >= -1) {
// 按钮被重新定制过需要过滤掉class防止覆盖 // 按钮被重新定制过需要过滤掉class防止覆盖
return s; return s;
} }
} }
return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`; return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`;
}, },
colorVariables: [...getThemeColors(), ...colors], colorVariables: [...getThemeColors(), ...colors],
}), }),
antdDarkThemePlugin({ antdDarkThemePlugin({
preloadFiles: [ preloadFiles: [
path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'), path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'),
//path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'), //path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'),
path.resolve(process.cwd(), 'src/design/index.less'), path.resolve(process.cwd(), 'src/design/index.less'),
], ],
filter: (id) => (isBuild ? !id.endsWith('antd.less') : true), filter: (id) => (isBuild ? !id.endsWith('antd.less') : true),
// extractCss: false, // extractCss: false,
darkModifyVars: { darkModifyVars: {
...generateModifyVars(true), ...generateModifyVars(true),
'text-color': '#c9d1d9', 'text-color': '#c9d1d9',
'primary-1': 'rgb(255 255 255 / 8%)', 'primary-1': 'rgb(255 255 255 / 8%)',
'text-color-base': '#c9d1d9', 'text-color-base': '#c9d1d9',
'component-background': '#151515', 'component-background': '#151515',
'heading-color': 'rgb(255 255 255 / 65%)', 'heading-color': 'rgb(255 255 255 / 65%)',
// black: '#0e1117', // black: '#0e1117',
// #8b949e // #8b949e
'text-color-secondary': '#8b949e', 'text-color-secondary': '#8b949e',
'border-color-base': '#303030', 'border-color-base': '#303030',
// 'border-color-split': '#30363d', // 'border-color-split': '#30363d',
'item-active-bg': '#111b26', 'item-active-bg': '#111b26',
'app-content-background': '#1e1e1e', 'app-content-background': '#1e1e1e',
'tree-node-selected-bg': '#11263c', 'tree-node-selected-bg': '#11263c',
'alert-success-border-color': '#274916', 'alert-success-border-color': '#274916',
'alert-success-bg-color': '#162312', 'alert-success-bg-color': '#162312',
'alert-success-icon-color': '#49aa19', 'alert-success-icon-color': '#49aa19',
'alert-info-border-color': '#153450', 'alert-info-border-color': '#153450',
'alert-info-bg-color': '#111b26', 'alert-info-bg-color': '#111b26',
'alert-info-icon-color': '#177ddc', 'alert-info-icon-color': '#177ddc',
'alert-warning-border-color': '#594214', 'alert-warning-border-color': '#594214',
'alert-warning-bg-color': '#2b2111', 'alert-warning-bg-color': '#2b2111',
'alert-warning-icon-color': '#d89614', 'alert-warning-icon-color': '#d89614',
'alert-error-border-color': '#58181c', 'alert-error-border-color': '#58181c',
'alert-error-bg-color': '#2a1215', 'alert-error-bg-color': '#2a1215',
'alert-error-icon-color': '#a61d24', 'alert-error-icon-color': '#a61d24',
}, },
}), }),
]; ];
return plugin as unknown as Plugin[]; return plugin as unknown as Plugin[];
} }

View File

@ -9,12 +9,7 @@ export function resultSuccess<T = Recordable>(result: T, { message = 'ok' } = {}
}; };
} }
export function resultPageSuccess<T = any>( export function resultPageSuccess<T = any>(pageNo: number, pageSize: number, list: T[], { message = 'ok' } = {}) {
pageNo: number,
pageSize: number,
list: T[],
{ message = 'ok' } = {}
) {
const pageData = pagination(pageNo, pageSize, list); const pageData = pagination(pageNo, pageSize, list);
return { return {
@ -37,10 +32,7 @@ export function resultError(message = 'Request failed', { code = -1, result = nu
export function pagination<T = any>(pageNo: number, pageSize: number, array: T[]): T[] { export function pagination<T = any>(pageNo: number, pageSize: number, array: T[]): T[] {
const offset = (pageNo - 1) * Number(pageSize); const offset = (pageNo - 1) * Number(pageSize);
const ret = const ret = offset + Number(pageSize) >= array.length ? array.slice(offset, array.length) : array.slice(offset, offset + Number(pageSize));
offset + Number(pageSize) >= array.length
? array.slice(offset, array.length)
: array.slice(offset, offset + Number(pageSize));
return ret; return ret;
} }

View File

@ -20,9 +20,9 @@ export default [
timeout: 1000, timeout: 1000,
method: 'get', method: 'get',
response: ({ query }) => { response: ({ query }) => {
const { keyword,count} = query; const { keyword, count } = query;
console.log(keyword); console.log(keyword);
return resultSuccess(demoList(keyword,count)); return resultSuccess(demoList(keyword, count));
}, },
}, },
] as MockMethod[]; ] as MockMethod[];

View File

@ -2,196 +2,183 @@ import { MockMethod } from 'vite-plugin-mock';
import { resultError, resultPageSuccess, resultSuccess, baseUrl } from '../_util'; import { resultError, resultPageSuccess, resultSuccess, baseUrl } from '../_util';
const accountList = (() => { const accountList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 20; index++) { for (let index = 0; index < 20; index++) {
result.push({ result.push({
id: `${index}`, id: `${index}`,
account: '@first', account: '@first',
email: '@email', email: '@email',
nickname: '@cname()', nickname: '@cname()',
role: '@first', role: '@first',
createTime: '@datetime', createTime: '@datetime',
remark: '@cword(10,20)', remark: '@cword(10,20)',
'status|1': ['0', '1'], 'status|1': ['0', '1'],
}); });
} }
return result; return result;
})(); })();
const userList = (() => { const userList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 20; index++) { for (let index = 0; index < 20; index++) {
result.push({ result.push({
id: `${index}`, id: `${index}`,
username: '@first', username: '@first',
email: '@email', email: '@email',
realname: '@cname()', realname: '@cname()',
createTime: '@datetime', createTime: '@datetime',
remark: '@cword(10,20)', remark: '@cword(10,20)',
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=640' avatar: 'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=640',
}); });
} }
return result; return result;
})(); })();
const roleList = (() => { const roleList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 4; index++) { for (let index = 0; index < 4; index++) {
result.push({ result.push({
id: index + 1, id: index + 1,
orderNo: `${index + 1}`, orderNo: `${index + 1}`,
roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index], roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index],
roleValue: '@first', roleValue: '@first',
createTime: '@datetime', createTime: '@datetime',
remark: '@cword(10,20)', remark: '@cword(10,20)',
menu: [['0', '1', '2'], ['0', '1'], ['0', '2'], ['2']][index], menu: [['0', '1', '2'], ['0', '1'], ['0', '2'], ['2']][index],
'status|1': ['0', '1'], 'status|1': ['0', '1'],
}); });
} }
return result; return result;
})(); })();
const newRoleList = (() => { const newRoleList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 4; index++) { for (let index = 0; index < 4; index++) {
result.push({ result.push({
id: index + 1, id: index + 1,
orderNo: `${index + 1}`, orderNo: `${index + 1}`,
roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index], roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index],
roleCode: '@first', roleCode: '@first',
createTime: '@datetime', createTime: '@datetime',
remark: '@cword(10,20)' remark: '@cword(10,20)',
}); });
} }
return result; return result;
})(); })();
const testList = (() => { const testList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 4; index++) { for (let index = 0; index < 4; index++) {
result.push({ result.push({
id: index + 1, id: index + 1,
orderNo: `${index + 1}`, orderNo: `${index + 1}`,
testName: ['数据1', '数据2', '数据3', '数据4'][index], testName: ['数据1', '数据2', '数据3', '数据4'][index],
testValue: '@first', testValue: '@first',
createTime: '@datetime' createTime: '@datetime',
}); });
} }
return result; return result;
})(); })();
const tableDemoList = (() => { const tableDemoList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 4; index++) { for (let index = 0; index < 4; index++) {
result.push({ result.push({
id: index + 1, id: index + 1,
orderCode: '2008200' + `${index + 1}`, orderCode: '2008200' + `${index + 1}`,
orderMoney: '@natural(1000,3000)', orderMoney: '@natural(1000,3000)',
ctype: '@natural(1,2)', ctype: '@natural(1,2)',
content: '@cword(10,20)', content: '@cword(10,20)',
orderDate: '@datetime' orderDate: '@datetime',
}); });
} }
return result; return result;
})(); })();
const deptList = (() => { const deptList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 3; index++) { for (let index = 0; index < 3; index++) {
result.push({ result.push({
id: `${index}`, id: `${index}`,
deptName: ['华东分部', '华南分部', '西北分部'][index], deptName: ['华东分部', '华南分部', '西北分部'][index],
orderNo: index + 1, orderNo: index + 1,
createTime: '@datetime',
remark: '@cword(10,20)',
'status|1': ['0', '0', '1'],
children: (() => {
const children: any[] = [];
for (let j = 0; j < 4; j++) {
children.push({
id: `${index}-${j}`,
deptName: ['研发部', '市场部', '商务部', '财务部'][j],
orderNo: j + 1,
createTime: '@datetime', createTime: '@datetime',
remark: '@cword(10,20)', remark: '@cword(10,20)',
'status|1': ['0', '0', '1'], 'status|1': ['0', '1'],
children: (() => { parentDept: `${index}`,
const children: any[] = []; children: undefined,
for (let j = 0; j < 4; j++) { });
children.push({ }
id: `${index}-${j}`, return children;
deptName: ['研发部', '市场部', '商务部', '财务部'][j], })(),
orderNo: j + 1, });
createTime: '@datetime', }
remark: '@cword(10,20)', return result;
'status|1': ['0', '1'],
parentDept: `${index}`,
children: undefined,
});
}
return children;
})(),
});
}
return result;
})(); })();
const menuList = (() => { const menuList = (() => {
const result: any[] = []; const result: any[] = [];
for (let index = 0; index < 3; index++) { for (let index = 0; index < 3; index++) {
result.push({ result.push({
id: `${index}`, id: `${index}`,
icon: ['ion:layers-outline', 'ion:git-compare-outline', 'ion:tv-outline'][index], icon: ['ion:layers-outline', 'ion:git-compare-outline', 'ion:tv-outline'][index],
component: 'LAYOUT', component: 'LAYOUT',
type: '0', type: '0',
menuName: ['Dashboard', '权限管理', '功能'][index], menuName: ['Dashboard', '权限管理', '功能'][index],
permission: '', permission: '',
orderNo: index + 1, orderNo: index + 1,
createTime: '@datetime',
'status|1': ['0', '0', '1'],
children: (() => {
const children: any[] = [];
for (let j = 0; j < 4; j++) {
children.push({
id: `${index}-${j}`,
type: '1',
menuName: ['菜单1', '菜单2', '菜单3', '菜单4'][j],
icon: 'ion:document',
permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index],
component: ['/dashboard/welcome/index', '/dashboard/Analysis/index', '/dashboard/workbench/index', '/dashboard/test/index'][j],
orderNo: j + 1,
createTime: '@datetime', createTime: '@datetime',
'status|1': ['0', '0', '1'], 'status|1': ['0', '1'],
parentMenu: `${index}`,
children: (() => { children: (() => {
const children: any[] = []; const children: any[] = [];
for (let j = 0; j < 4; j++) { for (let k = 0; k < 4; k++) {
children.push({ children.push({
id: `${index}-${j}`, id: `${index}-${j}-${k}`,
type: '1', type: '2',
menuName: ['菜单1', '菜单2', '菜单3', '菜单4'][j], menuName: '按钮' + (j + 1) + '-' + (k + 1),
icon: 'ion:document', icon: '',
permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index], permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index] + ':btn' + (k + 1),
component: [ component: ['/dashboard/welcome/index', '/dashboard/Analysis/index', '/dashboard/workbench/index', '/dashboard/test/index'][j],
'/dashboard/welcome/index', orderNo: j + 1,
'/dashboard/Analysis/index', createTime: '@datetime',
'/dashboard/workbench/index', 'status|1': ['0', '1'],
'/dashboard/test/index', parentMenu: `${index}-${j}`,
][j], children: undefined,
orderNo: j + 1, });
createTime: '@datetime', }
'status|1': ['0', '1'], return children;
parentMenu: `${index}`,
children: (() => {
const children: any[] = [];
for (let k = 0; k < 4; k++) {
children.push({
id: `${index}-${j}-${k}`,
type: '2',
menuName: '按钮' + (j + 1) + '-' + (k + 1),
icon: '',
permission:
['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index] +
':btn' +
(k + 1),
component: [
'/dashboard/welcome/index',
'/dashboard/Analysis/index',
'/dashboard/workbench/index',
'/dashboard/test/index',
][j],
orderNo: j + 1,
createTime: '@datetime',
'status|1': ['0', '1'],
parentMenu: `${index}-${j}`,
children: undefined,
});
}
return children;
})(),
});
}
return children;
})(), })(),
}); });
} }
return result; return children;
})(),
});
}
return result;
})(); })();
export default [ export default [

View File

@ -1,4 +1,4 @@
import { resultSuccess, resultError, getRequestToken, requestParams,baseUrl} from '../_util'; import { resultSuccess, resultError, getRequestToken, requestParams, baseUrl } from '../_util';
import { MockMethod } from 'vite-plugin-mock'; import { MockMethod } from 'vite-plugin-mock';
import { createFakeUserList } from './user'; import { createFakeUserList } from './user';

View File

@ -51,9 +51,7 @@ export default [
method: 'post', method: 'post',
response: ({ body }) => { response: ({ body }) => {
const { username, password } = body; const { username, password } = body;
const checkUser = createFakeUserList().find( const checkUser = createFakeUserList().find((item) => item.username === username && password === item.password);
(item) => item.username === username && password === item.password
);
if (!checkUser) { if (!checkUser) {
return resultError('Incorrect account or password'); return resultError('Incorrect account or password');
} }

View File

@ -291,7 +291,6 @@
"vue-print-nb-jeecg/src/printarea", "vue-print-nb-jeecg/src/printarea",
"vue-router", "vue-router",
"vue-types", "vue-types",
"vuedraggable",
"vxe-table", "vxe-table",
"vxe-table-plugin-antd", "vxe-table-plugin-antd",
"xe-utils", "xe-utils",

View File

@ -18,7 +18,7 @@ enum Api {
/** /**
* *
*/ */
export const uploadUrl=`${baseUploadUrl}/sys/common/upload`; export const uploadUrl = `${baseUploadUrl}/sys/common/upload`;
/** /**
* *
@ -47,52 +47,51 @@ export const getRoleList = (params) => {
/** /**
* *
*/ */
export const queryDepartTreeSync = (params?) =>{ export const queryDepartTreeSync = (params?) => {
return defHttp.get({ url: Api.queryDepartTreeSync, params }); return defHttp.get({ url: Api.queryDepartTreeSync, params });
} };
/** /**
* *
*/ */
export const queryTreeList = (params?) =>{ export const queryTreeList = (params?) => {
return defHttp.get({ url: Api.queryTreeList, params }); return defHttp.get({ url: Api.queryTreeList, params });
} };
/** /**
* *
*/ */
export const loadTreeData = (params?) =>{ export const loadTreeData = (params?) => {
return defHttp.get({ url: Api.loadTreeData, params }); return defHttp.get({ url: Api.loadTreeData, params });
} };
/** /**
* codetext * codetext
*/ */
export const loadDictItem = (params?) =>{ export const loadDictItem = (params?) => {
return defHttp.get({ url: Api.loadDictItem, params }); return defHttp.get({ url: Api.loadDictItem, params });
} };
/** /**
* codetext * codetext
*/ */
export const getDictItems = (dictCode) =>{ export const getDictItems = (dictCode) => {
return defHttp.get({ url: Api.getDictItems+dictCode},{joinTime:false}); return defHttp.get({ url: Api.getDictItems + dictCode }, { joinTime: false });
} };
/** /**
* modallist * modallist
*/ */
export const getTableList = (params)=>{ export const getTableList = (params) => {
return defHttp.get({url:Api.getTableList,params}) return defHttp.get({ url: Api.getTableList, params });
} };
/** /**
* *
*/ */
export const loadCategoryData = (params)=>{ export const loadCategoryData = (params) => {
return defHttp.get({url:Api.getCategoryData,params}) return defHttp.get({ url: Api.getCategoryData, params });
} };
/** /**
* *
*/ */
export const uploadFile = (params,success)=>{ export const uploadFile = (params, success) => {
return defHttp.uploadFile({url:uploadUrl}, params,{success}) return defHttp.uploadFile({ url: uploadUrl }, params, { success });
} };

View File

@ -7,5 +7,4 @@ enum Api {
/** /**
* @description: Get sample options value * @description: Get sample options value
*/ */
export const optionsListApi = (params?: selectParams) => export const optionsListApi = (params?: selectParams) => defHttp.get<DemoOptionsItem[]>({ url: Api.OPTIONS_LIST, params });
defHttp.get<DemoOptionsItem[]>({ url: Api.OPTIONS_LIST, params });

View File

@ -1,54 +1,45 @@
import { import {
AccountParams, AccountParams,
DeptListItem, DeptListItem,
MenuParams, MenuParams,
RoleParams, RoleParams,
TestPageParams, TestPageParams,
RolePageParams, RolePageParams,
MenuListGetResultModel, MenuListGetResultModel,
DeptListGetResultModel, DeptListGetResultModel,
AccountListGetResultModel, AccountListGetResultModel,
RolePageListGetResultModel, RolePageListGetResultModel,
RoleListGetResultModel, RoleListGetResultModel,
TestListGetResultModel TestListGetResultModel,
} from './model/systemModel'; } from './model/systemModel';
import {defHttp} from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
enum Api { enum Api {
AccountList = '/mock/system/getAccountList', AccountList = '/mock/system/getAccountList',
IsAccountExist = '/mock/system/accountExist', IsAccountExist = '/mock/system/accountExist',
DeptList = '/mock/system/getDeptList', DeptList = '/mock/system/getDeptList',
setRoleStatus = '/mock/system/setRoleStatus', setRoleStatus = '/mock/system/setRoleStatus',
MenuList = '/mock/system/getMenuList', MenuList = '/mock/system/getMenuList',
RolePageList = '/mock/system/getRoleListByPage', RolePageList = '/mock/system/getRoleListByPage',
DemoTableList = '/mock/system/getDemoTableListByPage', DemoTableList = '/mock/system/getDemoTableListByPage',
TestPageList = '/mock/system/getTestListByPage', TestPageList = '/mock/system/getTestListByPage',
GetAllRoleList = '/mock/system/getAllRoleList', GetAllRoleList = '/mock/system/getAllRoleList',
} }
export const getAccountList = (params: AccountParams) => export const getAccountList = (params: AccountParams) => defHttp.get<AccountListGetResultModel>({ url: Api.AccountList, params });
defHttp.get<AccountListGetResultModel>({url: Api.AccountList, params});
export const getDeptList = (params?: DeptListItem) => export const getDeptList = (params?: DeptListItem) => defHttp.get<DeptListGetResultModel>({ url: Api.DeptList, params });
defHttp.get<DeptListGetResultModel>({url: Api.DeptList, params});
export const getMenuList = (params?: MenuParams) => export const getMenuList = (params?: MenuParams) => defHttp.get<MenuListGetResultModel>({ url: Api.MenuList, params });
defHttp.get<MenuListGetResultModel>({url: Api.MenuList, params});
export const getRoleListByPage = (params?: RolePageParams) => export const getRoleListByPage = (params?: RolePageParams) => defHttp.get<RolePageListGetResultModel>({ url: Api.RolePageList, params });
defHttp.get<RolePageListGetResultModel>({url: Api.RolePageList, params});
export const getAllRoleList = (params?: RoleParams) => export const getAllRoleList = (params?: RoleParams) => defHttp.get<RoleListGetResultModel>({ url: Api.GetAllRoleList, params });
defHttp.get<RoleListGetResultModel>({url: Api.GetAllRoleList, params});
export const setRoleStatus = (id: number, status: string) => export const setRoleStatus = (id: number, status: string) => defHttp.post({ url: Api.setRoleStatus, params: { id, status } });
defHttp.post({url: Api.setRoleStatus, params: {id, status}});
export const getTestListByPage = (params?: TestPageParams) => export const getTestListByPage = (params?: TestPageParams) => defHttp.get<TestListGetResultModel>({ url: Api.TestPageList, params });
defHttp.get<TestListGetResultModel>({url: Api.TestPageList, params});
export const getDemoTableListByPage = (params) => export const getDemoTableListByPage = (params) => defHttp.get({ url: Api.DemoTableList, params });
defHttp.get({url: Api.DemoTableList, params});
export const isAccountExist = (account: string) => export const isAccountExist = (account: string) => defHttp.post({ url: Api.IsAccountExist, params: { account } }, { errorMessageMode: 'none' });
defHttp.post({url: Api.IsAccountExist, params: {account}}, {errorMessageMode: 'none'});

View File

@ -7,5 +7,4 @@ enum Api {
/** /**
* @description: Get sample options value * @description: Get sample options value
*/ */
export const treeOptionsListApi = (params?: Recordable) => export const treeOptionsListApi = (params?: Recordable) => defHttp.get<Recordable[]>({ url: Api.TREE_OPTIONS_LIST, params });
defHttp.get<Recordable[]>({ url: Api.TREE_OPTIONS_LIST, params });

View File

@ -12,12 +12,12 @@ enum Api {
export const getMenuList = () => { export const getMenuList = () => {
return new Promise((resolve) => { return new Promise((resolve) => {
//为了兼容mock和接口数据 //为了兼容mock和接口数据
defHttp.get<getMenuListResultModel>({ url: Api.GetMenuList }).then(res=>{ defHttp.get<getMenuListResultModel>({ url: Api.GetMenuList }).then((res) => {
if(Array.isArray(res)){ if (Array.isArray(res)) {
resolve(res) resolve(res);
}else{ } else {
resolve(res['menu']) resolve(res['menu']);
} }
}); });
}) });
}; };

View File

@ -52,6 +52,6 @@ export interface GetUserInfoModel {
export interface GetResultModel { export interface GetResultModel {
code: number; code: number;
message: string; message: string;
result:object; result: object;
success: Boolean; success: Boolean;
} }

View File

@ -8,10 +8,7 @@ const { uploadUrl = '' } = useGlobSetting();
/** /**
* @description: Upload interface * @description: Upload interface
*/ */
export function uploadApi( export function uploadApi(params: UploadFileParams, onUploadProgress: (progressEvent: ProgressEvent) => void) {
params: UploadFileParams,
onUploadProgress: (progressEvent: ProgressEvent) => void
) {
return defHttp.uploadFile<UploadApiResult>( return defHttp.uploadFile<UploadApiResult>(
{ {
url: uploadUrl, url: uploadUrl,
@ -23,15 +20,13 @@ export function uploadApi(
/** /**
* @description: Upload interface * @description: Upload interface
*/ */
export function uploadImg( export function uploadImg(params: UploadFileParams, onUploadProgress: (progressEvent: ProgressEvent) => void) {
params: UploadFileParams,
onUploadProgress: (progressEvent: ProgressEvent) => void
) {
return defHttp.uploadFile<UploadApiResult>( return defHttp.uploadFile<UploadApiResult>(
{ {
url: `${uploadUrl}/sys/common/upload`, url: `${uploadUrl}/sys/common/upload`,
onUploadProgress, onUploadProgress,
}, },
params, {isReturnResponse:true} params,
{ isReturnResponse: true }
); );
} }

View File

@ -2,16 +2,15 @@ import { defHttp } from '/@/utils/http/axios';
import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel'; import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel';
import { ErrorMessageMode } from '/#/axios'; import { ErrorMessageMode } from '/#/axios';
import {useMessage} from "/@/hooks/web/useMessage"; import { useMessage } from '/@/hooks/web/useMessage';
import {useUserStoreWithOut} from "/@/store/modules/user"; import { useUserStoreWithOut } from '/@/store/modules/user';
import {setAuthCache} from "/@/utils/auth"; import { setAuthCache } from '/@/utils/auth';
import {TOKEN_KEY} from "/@/enums/cacheEnum"; import { TOKEN_KEY } from '/@/enums/cacheEnum';
import {router} from "/@/router"; import { router } from '/@/router';
import {PageEnum} from "/@/enums/pageEnum"; import { PageEnum } from '/@/enums/pageEnum';
const { createErrorModal } = useMessage(); const { createErrorModal } = useMessage();
enum Api { enum Api {
Login = '/sys/login', Login = '/sys/login',
phoneLogin = '/sys/phoneLogin', phoneLogin = '/sys/phoneLogin',
Logout = '/sys/logout', Logout = '/sys/logout',
@ -23,7 +22,7 @@ enum Api {
GetPermCode = '/sys/permission/getPermCode', GetPermCode = '/sys/permission/getPermCode',
//新加的获取图形验证码的接口 //新加的获取图形验证码的接口
getInputCode = '/sys/randomImage', getInputCode = '/sys/randomImage',
//获取短信验证码的接口 //获取短信验证码的接口
getCaptcha = '/sys/sms', getCaptcha = '/sys/sms',
//注册接口 //注册接口
registerApi = '/sys/user/register', registerApi = '/sys/user/register',
@ -78,17 +77,17 @@ export function phoneLoginApi(params: LoginParams, mode: ErrorMessageMode = 'mod
/** /**
* @description: getUserInfo * @description: getUserInfo
*/ */
export function getUserInfo() { export function getUserInfo() {
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }).catch((e)=>{ return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }).catch((e) => {
// update-begin--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面 // update-begin--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
if (e && e.message.includes('timeout')) { if (e && e.message.includes('timeout')) {
//接口不通时跳转到登录界面 //接口不通时跳转到登录界面
const userStore = useUserStoreWithOut(); const userStore = useUserStoreWithOut();
userStore.setToken(''); userStore.setToken('');
setAuthCache(TOKEN_KEY, null); setAuthCache(TOKEN_KEY, null);
router.push(PageEnum.BASE_LOGIN); router.push(PageEnum.BASE_LOGIN);
} }
// update-end--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面 // update-end--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
}); });
} }
@ -101,7 +100,7 @@ export function doLogout() {
} }
export function getCodeInfo(currdatetime) { export function getCodeInfo(currdatetime) {
let url = Api.getInputCode+`/${currdatetime}` let url = Api.getInputCode + `/${currdatetime}`;
return defHttp.get({ url: url }); return defHttp.get({ url: url });
} }
/** /**
@ -109,78 +108,75 @@ export function getCodeInfo(currdatetime) {
*/ */
export function getCaptcha(params) { export function getCaptcha(params) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
defHttp.post({url:Api.getCaptcha,params},{isTransformResponse: false}).then(res=>{ defHttp.post({ url: Api.getCaptcha, params }, { isTransformResponse: false }).then((res) => {
console.log(res) console.log(res);
if(res.success){ if (res.success) {
resolve(true) resolve(true);
}else{ } else {
createErrorModal({ title: '错误提示', content: res.message||'未知问题' }); createErrorModal({ title: '错误提示', content: res.message || '未知问题' });
reject() reject();
} }
}); });
}) });
} }
/** /**
* @description: * @description:
*/ */
export function register(params) { export function register(params) {
return defHttp.post({url: Api.registerApi,params},{isReturnNativeResponse: true}) return defHttp.post({ url: Api.registerApi, params }, { isReturnNativeResponse: true });
} }
/** /**
* *
* @param params * @param params
*/ */
export const checkOnlyUser = (params) => export const checkOnlyUser = (params) => defHttp.get({ url: Api.checkOnlyUser, params }, { isTransformResponse: false });
defHttp.get({url: Api.checkOnlyUser, params},{isTransformResponse:false});
/** /**
* *
* @param params * @param params
*/ */
export const phoneVerify = (params) => export const phoneVerify = (params) => defHttp.post({ url: Api.phoneVerify, params }, { isTransformResponse: false });
defHttp.post({url: Api.phoneVerify, params},{isTransformResponse:false});
/** /**
* *
* @param params * @param params
*/ */
export const passwordChange = (params) => export const passwordChange = (params) => defHttp.get({ url: Api.passwordChange, params }, { isTransformResponse: false });
defHttp.get({url: Api.passwordChange, params},{isTransformResponse:false});
/** /**
* @description: * @description:
*/ */
export function thirdLogin(params, mode: ErrorMessageMode = 'modal') { export function thirdLogin(params, mode: ErrorMessageMode = 'modal') {
return defHttp.get<LoginResultModel>( return defHttp.get<LoginResultModel>(
{ {
url: `${Api.thirdLogin}/${params.token}/${params.thirdType}`, url: `${Api.thirdLogin}/${params.token}/${params.thirdType}`,
}, },
{ {
errorMessageMode: mode, errorMessageMode: mode,
} }
); );
} }
/** /**
* @description: * @description:
*/ */
export function setThirdCaptcha(params) { export function setThirdCaptcha(params) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
defHttp.post({url:Api.getThirdCaptcha,params},{isTransformResponse: false}).then(res=>{ defHttp.post({ url: Api.getThirdCaptcha, params }, { isTransformResponse: false }).then((res) => {
console.log(res) console.log(res);
if(res.success){ if (res.success) {
resolve(true) resolve(true);
}else{ } else {
createErrorModal({ title: '错误提示', content: res.message||'未知问题' }); createErrorModal({ title: '错误提示', content: res.message || '未知问题' });
reject() reject();
} }
}); });
}) });
} }
/** /**
* *
*/ */
export function getLoginQrcode() { export function getLoginQrcode() {
let url = Api.getLoginQrcode let url = Api.getLoginQrcode;
return defHttp.get({ url: url }); return defHttp.get({ url: url });
} }
@ -188,14 +184,14 @@ export function getLoginQrcode() {
* *
*/ */
export function getQrcodeToken(params) { export function getQrcodeToken(params) {
let url = Api.getQrcodeToken let url = Api.getQrcodeToken;
return defHttp.get({ url: url,params}); return defHttp.get({ url: url, params });
} }
/** /**
* SSO * SSO
*/ */
export async function validateCasLogin(params) { export async function validateCasLogin(params) {
let url = Api.validateCasLogin let url = Api.validateCasLogin;
return defHttp.get({ url: url,params}); return defHttp.get({ url: url, params });
} }

View File

@ -48,7 +48,6 @@
outline: 0; outline: 0;
} }
.area-select:active { .area-select:active {
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
} }
@ -96,12 +95,12 @@
top: 50%; top: 50%;
margin-top: -2px; margin-top: -2px;
right: 6px; right: 6px;
content: ""; content: '';
width: 0; width: 0;
height: 0; height: 0;
border: 6px solid transparent; border: 6px solid transparent;
border-top-color: rgba(0, 0, 0, 0.25); border-top-color: rgba(0, 0, 0, 0.25);
transition: all .3s linear; transition: all 0.3s linear;
transform-origin: center; transform-origin: center;
} }
@ -223,7 +222,7 @@
top: 50%; top: 50%;
margin-top: -4px; margin-top: -4px;
right: 5px; right: 5px;
content: ""; content: '';
width: 0; width: 0;
height: 0; height: 0;
border: 4px solid transparent; border: 4px solid transparent;
@ -256,4 +255,4 @@
.cascader-menu-list::-webkit-scrollbar-thumb:vertical:hover, .cascader-menu-list::-webkit-scrollbar-thumb:vertical:hover,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical:hover { .area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical:hover {
background-color: #777; background-color: #777;
} }

View File

@ -3,14 +3,7 @@
* @Description: Multi-language switching component * @Description: Multi-language switching component
--> -->
<template> <template>
<Dropdown <Dropdown placement="bottomCenter" :trigger="['click']" :dropMenuList="localeList" :selectedKeys="selectedKeys" @menuEvent="handleMenuEvent" overlayClassName="app-locale-picker-overlay">
placement="bottomCenter"
:trigger="['click']"
:dropMenuList="localeList"
:selectedKeys="selectedKeys"
@menuEvent="handleMenuEvent"
overlayClassName="app-locale-picker-overlay"
>
<span class="cursor-pointer flex items-center"> <span class="cursor-pointer flex items-center">
<Icon icon="ion:language" /> <Icon icon="ion:language" />
<span v-if="showText" class="ml-1">{{ getLocaleText }}</span> <span v-if="showText" class="ml-1">{{ getLocaleText }}</span>

View File

@ -40,11 +40,7 @@
const { title } = useGlobSetting(); const { title } = useGlobSetting();
const go = useGo(); const go = useGo();
const getAppLogoClass = computed(() => [ const getAppLogoClass = computed(() => [prefixCls, props.theme, { 'collapsed-show-title': unref(getCollapsedShowTitle) }]);
prefixCls,
props.theme,
{ 'collapsed-show-title': unref(getCollapsedShowTitle) },
]);
const getTitleClass = computed(() => [ const getTitleClass = computed(() => [
`${prefixCls}__title`, `${prefixCls}__title`,

View File

@ -45,12 +45,7 @@
if (!unref(isSetState)) { if (!unref(isSetState)) {
isSetState.value = true; isSetState.value = true;
const { const {
menuSetting: { menuSetting: { type: menuType, mode: menuMode, collapsed: menuCollapsed, split: menuSplit },
type: menuType,
mode: menuMode,
collapsed: menuCollapsed,
split: menuSplit,
},
} = appStore.getProjectConfig; } = appStore.getProjectConfig;
appStore.setProjectConfig({ appStore.setProjectConfig({
menuSetting: { menuSetting: {

View File

@ -41,8 +41,7 @@
margin-right: 0.4em; margin-right: 0.4em;
background-color: linear-gradient(-225deg, #d5dbe4, #f8f8f8); background-color: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
border-radius: 2px; border-radius: 2px;
box-shadow: inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff, box-shadow: inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
0 1px 2px 1px rgba(30, 35, 90, 0.4);
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@ -4,13 +4,7 @@
<div :class="getClass" @click.stop v-if="visible"> <div :class="getClass" @click.stop v-if="visible">
<div :class="`${prefixCls}-content`" v-click-outside="handleClose"> <div :class="`${prefixCls}-content`" v-click-outside="handleClose">
<div :class="`${prefixCls}-input__wrapper`"> <div :class="`${prefixCls}-input__wrapper`">
<a-input <a-input :class="`${prefixCls}-input`" :placeholder="t('common.searchText')" ref="inputRef" allow-clear @change="handleSearch">
:class="`${prefixCls}-input`"
:placeholder="t('common.searchText')"
ref="inputRef"
allow-clear
@change="handleSearch"
>
<template #prefix> <template #prefix>
<SearchOutlined /> <SearchOutlined />
</template> </template>
@ -26,13 +20,13 @@
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap"> <ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
<li <li
:ref="setRefs(index)" :ref="setRefs(index)"
v-for="(item, index) in searchResult" v-for="(item, index) in searchResult"
:key="item.path" :key="item.path"
:data-index="index" :data-index="index"
@mouseenter="handleMouseenter" @mouseenter="handleMouseenter"
@click="handleEnter" @click="handleEnter"
:class="[ :class="[
`${prefixCls}-list__item`, `${prefixCls}-list__item`,
{ {
[`${prefixCls}-list__item--active`]: activeIndex === index, [`${prefixCls}-list__item--active`]: activeIndex === index,
@ -84,8 +78,7 @@
const [refs, setRefs] = useRefs(); const [refs, setRefs] = useRefs();
const { getIsMobile } = useAppInject(); const { getIsMobile } = useAppInject();
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } = const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } = useMenuSearch(refs, scrollWrap, emit);
useMenuSearch(refs, scrollWrap, emit);
const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0); const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0);
@ -99,13 +92,13 @@
}); });
watch( watch(
() => props.visible, () => props.visible,
(visible: boolean) => { (visible: boolean) => {
visible && visible &&
nextTick(() => { nextTick(() => {
unref(inputRef)?.focus(); unref(inputRef)?.focus();
}); });
} }
); );
function handleClose() { function handleClose() {

View File

@ -57,7 +57,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
const filterMenu = filter(menuList, (item) => { const filterMenu = filter(menuList, (item) => {
// 【issues/33】包含子菜单时不添加到搜索队列 // 【issues/33】包含子菜单时不添加到搜索队列
if (Array.isArray(item.children)) { if (Array.isArray(item.children)) {
return false return false;
} }
return reg.test(item.name) && !item.hideMenu; return reg.test(item.name) && !item.hideMenu;
}); });

View File

@ -46,9 +46,7 @@
setup(props, { slots }) { setup(props, { slots }) {
const { prefixCls } = useDesign('basic-help'); const { prefixCls } = useDesign('basic-help');
const getTooltipStyle = computed( const getTooltipStyle = computed((): CSSProperties => ({ color: props.color, fontSize: props.fontSize }));
(): CSSProperties => ({ color: props.color, fontSize: props.fontSize })
);
const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth })); const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth }));

View File

@ -33,11 +33,7 @@
const { prefixCls } = useDesign('basic-title'); const { prefixCls } = useDesign('basic-title');
const slots = useSlots(); const slots = useSlots();
const getClass = computed(() => [ const getClass = computed(() => [prefixCls, { [`${prefixCls}-show-span`]: props.span && slots.default }, { [`${prefixCls}-normal`]: props.normal }]);
prefixCls,
{ [`${prefixCls}-show-span`]: props.span && slots.default },
{ [`${prefixCls}-normal`]: props.normal },
]);
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-basic-title'; @prefix-cls: ~'@{namespace}-basic-title';

View File

@ -1,39 +1,39 @@
<template> <template>
<Button v-bind="getBindValue" :class="getButtonClass" @click="onClick"> <Button v-bind="getBindValue" :class="getButtonClass" @click="onClick">
<template #default="data"> <template #default="data">
<Icon :icon="preIcon" v-if="preIcon" :size="iconSize" /> <Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
<slot v-bind="data || {}"></slot> <slot v-bind="data || {}"></slot>
<Icon :icon="postIcon" v-if="postIcon" :size="iconSize" /> <Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
</template> </template>
</Button> </Button>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: 'AButton', name: 'AButton',
inheritAttrs: false, inheritAttrs: false,
}); });
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, unref } from 'vue'; import { computed, unref } from 'vue';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import Icon from '/@/components/Icon/src/Icon.vue'; import Icon from '/@/components/Icon/src/Icon.vue';
import { buttonProps } from './props'; import { buttonProps } from './props';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
const props = defineProps(buttonProps); const props = defineProps(buttonProps);
// get component class // get component class
const attrs = useAttrs({ excludeDefaultKeys: false }); const attrs = useAttrs({ excludeDefaultKeys: false });
const getButtonClass = computed(() => { const getButtonClass = computed(() => {
const { color, disabled } = props; const { color, disabled } = props;
return [ return [
{ {
[`ant-btn-${color}`]: !!color, [`ant-btn-${color}`]: !!color,
[`is-disabled`]: disabled, [`is-disabled`]: disabled,
}, },
]; ];
}); });
// get inherit binding value // get inherit binding value
const getBindValue = computed(() => ({ ...unref(attrs), ...props })); const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
</script> </script>

View File

@ -1,54 +1,54 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, h, unref } from 'vue'; import { computed, defineComponent, h, unref } from 'vue';
import BasicButton from './BasicButton.vue'; import BasicButton from './BasicButton.vue';
import { Popconfirm } from 'ant-design-vue'; import { Popconfirm } from 'ant-design-vue';
import { extendSlots } from '/@/utils/helper/tsxHelper'; import { extendSlots } from '/@/utils/helper/tsxHelper';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
const props = { const props = {
/** /**
* Whether to enable the drop-down menu * Whether to enable the drop-down menu
* @default: true * @default: true
*/ */
enable: { enable: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
}; };
export default defineComponent({ export default defineComponent({
name: 'PopButton', name: 'PopButton',
inheritAttrs: false, inheritAttrs: false,
props, props,
setup(props, { slots }) { setup(props, { slots }) {
const { t } = useI18n(); const { t } = useI18n();
const attrs = useAttrs(); const attrs = useAttrs();
// get inherit binding value // get inherit binding value
const getBindValues = computed(() => { const getBindValues = computed(() => {
return Object.assign( return Object.assign(
{ {
okText: t('common.okText'), okText: t('common.okText'),
cancelText: t('common.cancelText'), cancelText: t('common.cancelText'),
}, },
{ ...props, ...unref(attrs) } { ...props, ...unref(attrs) }
); );
}); });
return () => { return () => {
const bindValues = omit(unref(getBindValues), 'icon'); const bindValues = omit(unref(getBindValues), 'icon');
const btnBind = omit(bindValues, 'title') as Recordable; const btnBind = omit(bindValues, 'title') as Recordable;
if (btnBind.disabled) btnBind.color = ''; if (btnBind.disabled) btnBind.color = '';
const Button = h(BasicButton, btnBind, extendSlots(slots)); const Button = h(BasicButton, btnBind, extendSlots(slots));
// If it is not enabled, it is a normal button // If it is not enabled, it is a normal button
if (!props.enable) { if (!props.enable) {
return Button; return Button;
} }
return h(Popconfirm, bindValues, { default: () => Button }); return h(Popconfirm, bindValues, { default: () => Button });
}; };
}, },
}); });
</script> </script>

View File

@ -1,41 +1,41 @@
<template> <template>
<a-upload name="file" :showUploadList="false" :customRequest="(file)=>onClick(file)"> <a-upload name="file" :showUploadList="false" :customRequest="(file) => onClick(file)">
<Button :type="type" :class="getButtonClass" > <Button :type="type" :class="getButtonClass">
<template #default="data"> <template #default="data">
<Icon :icon="preIcon" v-if="preIcon" :size="iconSize" /> <Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
<slot v-bind="data || {}"></slot> <slot v-bind="data || {}"></slot>
<Icon :icon="postIcon" v-if="postIcon" :size="iconSize" /> <Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
</template> </template>
</Button> </Button>
</a-upload> </a-upload>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: 'JUploadButton', name: 'JUploadButton',
inheritAttrs: false, inheritAttrs: false,
}); });
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, unref } from 'vue'; import { computed, unref } from 'vue';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import Icon from '/@/components/Icon/src/Icon.vue'; import Icon from '/@/components/Icon/src/Icon.vue';
import { buttonProps } from './props'; import { buttonProps } from './props';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
const props = defineProps(buttonProps); const props = defineProps(buttonProps);
// get component class // get component class
const attrs = useAttrs({ excludeDefaultKeys: false }); const attrs = useAttrs({ excludeDefaultKeys: false });
const getButtonClass = computed(() => { const getButtonClass = computed(() => {
const { color, disabled } = props; const { color, disabled } = props;
return [ return [
{ {
[`ant-btn-${color}`]: !!color, [`ant-btn-${color}`]: !!color,
[`is-disabled`]: disabled, [`is-disabled`]: disabled,
}, },
]; ];
}); });
// get inherit binding value // get inherit binding value
const getBindValue = computed(() => ({ ...unref(attrs), ...props })); const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
</script> </script>

View File

@ -16,6 +16,6 @@ export const buttonProps = {
* @default: 14 * @default: 14
*/ */
iconSize: { type: Number, default: 14 }, iconSize: { type: Number, default: 14 },
isUpload:{type:Boolean,default:false}, isUpload: { type: Boolean, default: false },
onClick: { type: Function as PropType<(...args) => any>, default: null }, onClick: { type: Function as PropType<(...args) => any>, default: null },
}; };

View File

@ -1,50 +1,39 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<div class="bg-white mb-2 p-4"> <div class="bg-white mb-2 p-4">
<BasicForm @register="registerForm" /> <BasicForm @register="registerForm" />
</div> </div>
{{ sliderProp.width }} {{ sliderProp.width }}
<div class="bg-white p-2"> <div class="bg-white p-2">
<List <List :grid="{ gutter: 5, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: grid }" :data-source="data" :pagination="paginationProp">
:grid="{ gutter: 5, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: grid }" <template #header>
:data-source="data" <div class="flex justify-end space-x-2"
:pagination="paginationProp" ><slot name="header"></slot>
> <Tooltip>
<template #header> <template #title> <div class="w-50">每行显示数量</div><Slider id="slider" v-bind="sliderProp" v-model:value="grid" @change="sliderChange" /></template>
<div class="flex justify-end space-x-2" <Button><TableOutlined /></Button>
><slot name="header"></slot> </Tooltip>
<Tooltip> <Tooltip @click="fetch">
<template #title> <template #title>刷新</template>
<div class="w-50">每行显示数量</div <Button><RedoOutlined /></Button>
><Slider </Tooltip>
id="slider" </div>
v-bind="sliderProp" </template>
v-model:value="grid" <template #renderItem="{ item }">
@change="sliderChange" <ListItem>
/></template> <Card>
<Button><TableOutlined /></Button> <template #title></template>
</Tooltip> <template #cover>
<Tooltip @click="fetch"> <div :class="height">
<template #title>刷新</template> <Image :src="item.imgs[0]" />
<Button><RedoOutlined /></Button> </div>
</Tooltip> </template>
</div> <template class="ant-card-actions" #actions>
</template> <!-- <SettingOutlined key="setting" />-->
<template #renderItem="{ item }"> <EditOutlined key="edit" />
<ListItem> <Dropdown
<Card> :trigger="['hover']"
<template #title></template> :dropMenuList="[
<template #cover>
<div :class="height">
<Image :src="item.imgs[0]" />
</div>
</template>
<template class="ant-card-actions" #actions>
<!-- <SettingOutlined key="setting" />-->
<EditOutlined key="edit" />
<Dropdown
:trigger="['hover']"
:dropMenuList="[
{ {
text: '删除', text: '删除',
event: '1', event: '1',
@ -54,125 +43,120 @@
}, },
}, },
]" ]"
popconfirm popconfirm
> >
<EllipsisOutlined key="ellipsis" /> <EllipsisOutlined key="ellipsis" />
</Dropdown> </Dropdown>
</template> </template>
<CardMeta> <CardMeta>
<template #title> <template #title>
<TypographyText :content="item.name" :ellipsis="{ tooltip: item.address }" /> <TypographyText :content="item.name" :ellipsis="{ tooltip: item.address }" />
</template>
<template #avatar>
<Avatar :src="item.avatar" />
</template>
<template #description>{{ item.time }}</template>
</CardMeta>
</Card>
</ListItem>
</template> </template>
</List> <template #avatar>
</div> <Avatar :src="item.avatar" />
</template>
<template #description>{{ item.time }}</template>
</CardMeta>
</Card>
</ListItem>
</template>
</List>
</div> </div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { import { EditOutlined, EllipsisOutlined, RedoOutlined, TableOutlined } from '@ant-design/icons-vue';
EditOutlined, import { List, Card, Image, Typography, Tooltip, Slider, Avatar } from 'ant-design-vue';
EllipsisOutlined, import { Dropdown } from '/@/components/Dropdown';
RedoOutlined, import { BasicForm, useForm } from '/@/components/Form';
TableOutlined, import { propTypes } from '/@/utils/propTypes';
} from '@ant-design/icons-vue'; import { Button } from '/@/components/Button';
import { List, Card, Image, Typography, Tooltip, Slider, Avatar } from 'ant-design-vue'; import { isFunction } from '/@/utils/is';
import { Dropdown } from '/@/components/Dropdown'; import { useSlider, grid } from './data';
import { BasicForm, useForm } from '/@/components/Form'; const ListItem = List.Item;
import { propTypes } from '/@/utils/propTypes'; const CardMeta = Card.Meta;
import { Button } from '/@/components/Button'; const TypographyText = Typography.Text;
import { isFunction } from '/@/utils/is'; // slider
import { useSlider, grid } from './data'; const sliderProp = computed(() => useSlider(4));
const ListItem = List.Item; //
const CardMeta = Card.Meta; const props = defineProps({
const TypographyText = Typography.Text; // API
// slider params: propTypes.object.def({}),
const sliderProp = computed(() => useSlider(4)); //api
// api: propTypes.func,
const props = defineProps({ });
// API //
params: propTypes.object.def({}), const emit = defineEmits(['getMethod', 'delete']);
//api //
api: propTypes.func, const data = ref([]);
}); //
// // cover
const emit = defineEmits(['getMethod', 'delete']); //pageSize
//
const data = ref([]);
//
// cover
//pageSize
const height = computed(() => { const height = computed(() => {
return `h-${120 - grid.value * 6}`; return `h-${120 - grid.value * 6}`;
}); });
// //
const [registerForm, { validate }] = useForm({ const [registerForm, { validate }] = useForm({
schemas: [{ field: 'type', component: 'Input', label: '类型' }], schemas: [{ field: 'type', component: 'Input', label: '类型' }],
labelWidth: 80, labelWidth: 80,
baseColProps: { span: 6 }, baseColProps: { span: 6 },
actionColOptions: { span: 24 }, actionColOptions: { span: 24 },
autoSubmitOnEnter: true, autoSubmitOnEnter: true,
submitFunc: handleSubmit, submitFunc: handleSubmit,
}); });
// //
async function handleSubmit() { async function handleSubmit() {
const data = await validate(); const data = await validate();
await fetch(data); await fetch(data);
} }
function sliderChange(n) { function sliderChange(n) {
pageSize.value = n * 4; pageSize.value = n * 4;
fetch(); fetch();
} }
// //
onMounted(() => { onMounted(() => {
fetch(); fetch();
emit('getMethod', fetch); emit('getMethod', fetch);
}); });
async function fetch(p = {}) { async function fetch(p = {}) {
const { api, params } = props; const { api, params } = props;
if (api && isFunction(api)) { if (api && isFunction(api)) {
const res = await api({ ...params, page: page.value, pageSize: pageSize.value, ...p }); const res = await api({ ...params, page: page.value, pageSize: pageSize.value, ...p });
data.value = res.items; data.value = res.items;
total.value = res.total; total.value = res.total;
}
} }
// }
const page = ref(1); //
const pageSize = ref(36); const page = ref(1);
const total = ref(0); const pageSize = ref(36);
const paginationProp = ref({ const total = ref(0);
showSizeChanger: false, const paginationProp = ref({
showQuickJumper: true, showSizeChanger: false,
pageSize, showQuickJumper: true,
current: page, pageSize,
total, current: page,
showTotal: (total) => `${total}`, total,
onChange: pageChange, showTotal: (total) => `${total}`,
onShowSizeChange: pageSizeChange, onChange: pageChange,
}); onShowSizeChange: pageSizeChange,
});
function pageChange(p, pz) { function pageChange(p, pz) {
page.value = p; page.value = p;
pageSize.value = pz; pageSize.value = pz;
fetch(); fetch();
} }
function pageSizeChange(current, size) { function pageSizeChange(current, size) {
pageSize.value = size; pageSize.value = size;
fetch(); fetch();
} }
async function handleDelete(id) { async function handleDelete(id) {
emit('delete', id); emit('delete', id);
} }
</script> </script>

View File

@ -1,11 +1,6 @@
<template> <template>
<div class="h-full"> <div class="h-full">
<CodeMirrorEditor <CodeMirrorEditor :value="getValue" @change="handleValueChange" :mode="mode" :readonly="readonly" />
:value="getValue"
@change="handleValueChange"
:mode="mode"
:readonly="readonly"
/>
</div> </div>
</template> </template>

View File

@ -32,15 +32,15 @@
const appStore = useAppStore(); const appStore = useAppStore();
watch( watch(
() => props.value, () => props.value,
async (value) => { async (value) => {
await nextTick(); await nextTick();
const oldValue = editor?.getValue(); const oldValue = editor?.getValue();
if (value !== oldValue) { if (value !== oldValue) {
editor?.setValue(value ? value : ''); editor?.setValue(value ? value : '');
} }
}, },
{ flush: 'post' } { flush: 'post' }
); );
watchEffect(() => { watchEffect(() => {
@ -48,20 +48,17 @@
}); });
watch( watch(
() => appStore.getDarkMode, () => appStore.getDarkMode,
async () => { async () => {
setTheme(); setTheme();
}, },
{ {
immediate: true, immediate: true,
} }
); );
function setTheme() { function setTheme() {
unref(editor)?.setOption( unref(editor)?.setOption('theme', appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight');
'theme',
appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight'
);
} }
function refresh() { function refresh() {

View File

@ -1,12 +1,5 @@
<template> <template>
<transition-group <transition-group class="h-full w-full" v-bind="$attrs" ref="elRef" :name="transitionName" :tag="tag" mode="out-in">
class="h-full w-full"
v-bind="$attrs"
ref="elRef"
:name="transitionName"
:tag="tag"
mode="out-in"
>
<div key="component" v-if="isInit"> <div key="component" v-if="isInit">
<slot :loading="loading"></slot> <slot :loading="loading"></slot>
</div> </div>

View File

@ -1,207 +1,196 @@
<script lang="tsx"> <script lang="tsx">
import type { ContextMenuItem, ItemContentProps, Axis } from './typing'; import type { ContextMenuItem, ItemContentProps, Axis } from './typing';
import type { FunctionalComponent, CSSProperties } from 'vue'; import type { FunctionalComponent, CSSProperties } from 'vue';
import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue'; import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue';
import Icon from '/@/components/Icon'; import Icon from '/@/components/Icon';
import { Menu, Divider } from 'ant-design-vue'; import { Menu, Divider } from 'ant-design-vue';
const prefixCls = 'context-menu'; const prefixCls = 'context-menu';
const props = { const props = {
width: { type: Number, default: 156 }, width: { type: Number, default: 156 },
customEvent: { type: Object as PropType<Event>, default: null }, customEvent: { type: Object as PropType<Event>, default: null },
styles: { type: Object as PropType<CSSProperties> }, styles: { type: Object as PropType<CSSProperties> },
showIcon: { type: Boolean, default: true }, showIcon: { type: Boolean, default: true },
axis: { axis: {
// The position of the right mouse button click // The position of the right mouse button click
type: Object as PropType<Axis>, type: Object as PropType<Axis>,
default() { default() {
return { x: 0, y: 0 }; return { x: 0, y: 0 };
}, },
}, },
items: { items: {
// The most important list, if not, will not be displayed // The most important list, if not, will not be displayed
type: Array as PropType<ContextMenuItem[]>, type: Array as PropType<ContextMenuItem[]>,
default() { default() {
return []; return [];
}, },
}, },
}; };
const ItemContent: FunctionalComponent<ItemContentProps> = (props) => { const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
const { item } = props; const { item } = props;
return ( return (
<span <span style="display: inline-block; width: 100%; " class="px-4" onClick={props.handler.bind(null, item)}>
style="display: inline-block; width: 100%; "
class="px-4"
onClick={props.handler.bind(null, item)}
>
{props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />} {props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />}
<span>{item.label}</span> <span>{item.label}</span>
</span> </span>
);
};
export default defineComponent({
name: 'ContextMenu',
props,
setup(props) {
const wrapRef = ref(null);
const showRef = ref(false);
const getStyle = computed((): CSSProperties => {
const { axis, items, styles, width } = props;
const { x, y } = axis || { x: 0, y: 0 };
const menuHeight = (items || []).length * 40;
const menuWidth = width;
const body = document.body;
const left = body.clientWidth < x + menuWidth ? x - menuWidth : x;
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
return {
...styles,
width: `${width}px`,
left: `${left + 1}px`,
top: `${top + 1}px`,
};
});
onMounted(() => {
nextTick(() => (showRef.value = true));
});
onUnmounted(() => {
const el = unref(wrapRef);
el && document.body.removeChild(el);
});
function handleAction(item: ContextMenuItem, e: MouseEvent) {
const { handler, disabled } = item;
if (disabled) {
return;
}
showRef.value = false;
e?.stopPropagation();
e?.preventDefault();
handler?.();
}
function renderMenuItem(items: ContextMenuItem[]) {
return items.map((item) => {
const { disabled, label, children, divider = false } = item;
const contentProps = {
item,
handler: handleAction,
showIcon: props.showIcon,
};
if (!children || children.length === 0) {
return (
<>
<Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}>
<ItemContent {...contentProps} />
</Menu.Item>
{divider ? <Divider key={`d-${label}`} /> : null}
</>
);
}
if (!unref(showRef)) return null;
return (
<Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}>
{{
title: () => <ItemContent {...contentProps} />,
default: () => renderMenuItem(children),
}}
</Menu.SubMenu>
);
});
}
return () => {
if (!unref(showRef)) {
return null;
}
const { items } = props;
return (
<Menu inlineIndent={12} mode="vertical" class={prefixCls} ref={wrapRef} style={unref(getStyle)}>
{renderMenuItem(items)}
</Menu>
); );
}; };
},
export default defineComponent({ });
name: 'ContextMenu',
props,
setup(props) {
const wrapRef = ref(null);
const showRef = ref(false);
const getStyle = computed((): CSSProperties => {
const { axis, items, styles, width } = props;
const { x, y } = axis || { x: 0, y: 0 };
const menuHeight = (items || []).length * 40;
const menuWidth = width;
const body = document.body;
const left = body.clientWidth < x + menuWidth ? x - menuWidth : x;
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
return {
...styles,
width: `${width}px`,
left: `${left + 1}px`,
top: `${top + 1}px`,
};
});
onMounted(() => {
nextTick(() => (showRef.value = true));
});
onUnmounted(() => {
const el = unref(wrapRef);
el && document.body.removeChild(el);
});
function handleAction(item: ContextMenuItem, e: MouseEvent) {
const { handler, disabled } = item;
if (disabled) {
return;
}
showRef.value = false;
e?.stopPropagation();
e?.preventDefault();
handler?.();
}
function renderMenuItem(items: ContextMenuItem[]) {
return items.map((item) => {
const { disabled, label, children, divider = false } = item;
const contentProps = {
item,
handler: handleAction,
showIcon: props.showIcon,
};
if (!children || children.length === 0) {
return (
<>
<Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}>
<ItemContent {...contentProps} />
</Menu.Item>
{divider ? <Divider key={`d-${label}`} /> : null}
</>
);
}
if (!unref(showRef)) return null;
return (
<Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}>
{{
title: () => <ItemContent {...contentProps} />,
default: () => renderMenuItem(children),
}}
</Menu.SubMenu>
);
});
}
return () => {
if (!unref(showRef)) {
return null;
}
const { items } = props;
return (
<Menu
inlineIndent={12}
mode="vertical"
class={prefixCls}
ref={wrapRef}
style={unref(getStyle)}
>
{renderMenuItem(items)}
</Menu>
);
};
},
});
</script> </script>
<style lang="less"> <style lang="less">
@default-height: 42px !important; @default-height: 42px !important;
@small-height: 36px !important; @small-height: 36px !important;
@large-height: 36px !important; @large-height: 36px !important;
.item-style() { .item-style() {
li { li {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
height: @default-height; height: @default-height;
margin: 0 !important; margin: 0 !important;
line-height: @default-height; line-height: @default-height;
span { span {
line-height: @default-height; line-height: @default-height;
} }
> div { > div {
margin: 0 !important; margin: 0 !important;
} }
&:not(.ant-menu-item-disabled):hover { &:not(.ant-menu-item-disabled):hover {
color: @text-color-base; color: @text-color-base;
background-color: @item-hover-bg; background-color: @item-hover-bg;
} }
} }
}
.context-menu {
position: fixed;
top: 0;
left: 0;
z-index: 200;
display: block;
width: 156px;
margin: 0;
list-style: none;
background-color: @component-background;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 0.25rem;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.1), 0 1px 5px 0 rgba(0, 0, 0, 0.06);
background-clip: padding-box;
user-select: none;
.item-style();
.ant-divider {
margin: 0 0;
} }
.context-menu { &__popup {
position: fixed; .ant-divider {
top: 0; margin: 0 0;
left: 0; }
z-index: 200;
display: block;
width: 156px;
margin: 0;
list-style: none;
background-color: @component-background;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 0.25rem;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.1),
0 1px 5px 0 rgba(0, 0, 0, 0.06);
background-clip: padding-box;
user-select: none;
.item-style(); .item-style();
.ant-divider {
margin: 0 0;
}
&__popup {
.ant-divider {
margin: 0 0;
}
.item-style();
}
.ant-menu-submenu-title,
.ant-menu-item {
padding: 0 !important;
}
} }
.ant-menu-submenu-title,
.ant-menu-item {
padding: 0 !important;
}
}
</style> </style>

View File

@ -30,9 +30,7 @@
const { t } = useI18n(); const { t } = useI18n();
const getButtonText = computed(() => { const getButtonText = computed(() => {
return !unref(isStart) return !unref(isStart) ? t('component.countdown.normalText') : t('component.countdown.sendText', [unref(currentCount)]);
? t('component.countdown.normalText')
: t('component.countdown.sendText', [unref(currentCount)]);
}); });
watchEffect(() => { watchEffect(() => {

View File

@ -1,283 +1,214 @@
<template> <template>
<BasicModal <BasicModal v-bind="$attrs" @register="register" :title="t('component.cropper.modalTitle')" width="800px" :canFullscreen="false" @ok="handleOk" :okText="t('component.cropper.okText')">
v-bind="$attrs" <div :class="prefixCls">
@register="register" <div :class="`${prefixCls}-left`">
:title="t('component.cropper.modalTitle')" <div :class="`${prefixCls}-cropper`">
width="800px" <CropperImage v-if="src" :src="src" height="300px" :circled="circled" @cropend="handleCropend" @ready="handleReady" />
:canFullscreen="false"
@ok="handleOk"
:okText="t('component.cropper.okText')"
>
<div :class="prefixCls">
<div :class="`${prefixCls}-left`">
<div :class="`${prefixCls}-cropper`">
<CropperImage
v-if="src"
:src="src"
height="300px"
:circled="circled"
@cropend="handleCropend"
@ready="handleReady"
/>
</div>
<div :class="`${prefixCls}-toolbar`">
<Upload :fileList="[]" accept="image/*" :beforeUpload="handleBeforeUpload">
<Tooltip :title="t('component.cropper.selectImage')" placement="bottom">
<a-button size="small" preIcon="ant-design:upload-outlined" type="primary" />
</Tooltip>
</Upload>
<Space>
<Tooltip :title="t('component.cropper.btn_reset')" placement="bottom">
<a-button
type="primary"
preIcon="ant-design:reload-outlined"
size="small"
:disabled="!src"
@click="handlerToolbar('reset')"
/>
</Tooltip>
<Tooltip :title="t('component.cropper.btn_rotate_left')" placement="bottom">
<a-button
type="primary"
preIcon="ant-design:rotate-left-outlined"
size="small"
:disabled="!src"
@click="handlerToolbar('rotate', -45)"
/>
</Tooltip>
<Tooltip :title="t('component.cropper.btn_rotate_right')" placement="bottom">
<a-button
type="primary"
preIcon="ant-design:rotate-right-outlined"
size="small"
:disabled="!src"
@click="handlerToolbar('rotate', 45)"
/>
</Tooltip>
<Tooltip :title="t('component.cropper.btn_scale_x')" placement="bottom">
<a-button
type="primary"
preIcon="vaadin:arrows-long-h"
size="small"
:disabled="!src"
@click="handlerToolbar('scaleX')"
/>
</Tooltip>
<Tooltip :title="t('component.cropper.btn_scale_y')" placement="bottom">
<a-button
type="primary"
preIcon="vaadin:arrows-long-v"
size="small"
:disabled="!src"
@click="handlerToolbar('scaleY')"
/>
</Tooltip>
<Tooltip :title="t('component.cropper.btn_zoom_in')" placement="bottom">
<a-button
type="primary"
preIcon="ant-design:zoom-in-outlined"
size="small"
:disabled="!src"
@click="handlerToolbar('zoom', 0.1)"
/>
</Tooltip>
<Tooltip :title="t('component.cropper.btn_zoom_out')" placement="bottom">
<a-button
type="primary"
preIcon="ant-design:zoom-out-outlined"
size="small"
:disabled="!src"
@click="handlerToolbar('zoom', -0.1)"
/>
</Tooltip>
</Space>
</div>
</div>
<div :class="`${prefixCls}-right`">
<div :class="`${prefixCls}-preview`">
<img :src="previewSource" v-if="previewSource" :alt="t('component.cropper.preview')" />
</div>
<template v-if="previewSource">
<div :class="`${prefixCls}-group`">
<Avatar :src="previewSource" size="large" />
<Avatar :src="previewSource" :size="48" />
<Avatar :src="previewSource" :size="64" />
<Avatar :src="previewSource" :size="80" />
</div>
</template>
</div>
</div> </div>
</BasicModal>
<div :class="`${prefixCls}-toolbar`">
<Upload :fileList="[]" accept="image/*" :beforeUpload="handleBeforeUpload">
<Tooltip :title="t('component.cropper.selectImage')" placement="bottom">
<a-button size="small" preIcon="ant-design:upload-outlined" type="primary" />
</Tooltip>
</Upload>
<Space>
<Tooltip :title="t('component.cropper.btn_reset')" placement="bottom">
<a-button type="primary" preIcon="ant-design:reload-outlined" size="small" :disabled="!src" @click="handlerToolbar('reset')" />
</Tooltip>
<Tooltip :title="t('component.cropper.btn_rotate_left')" placement="bottom">
<a-button type="primary" preIcon="ant-design:rotate-left-outlined" size="small" :disabled="!src" @click="handlerToolbar('rotate', -45)" />
</Tooltip>
<Tooltip :title="t('component.cropper.btn_rotate_right')" placement="bottom">
<a-button type="primary" preIcon="ant-design:rotate-right-outlined" size="small" :disabled="!src" @click="handlerToolbar('rotate', 45)" />
</Tooltip>
<Tooltip :title="t('component.cropper.btn_scale_x')" placement="bottom">
<a-button type="primary" preIcon="vaadin:arrows-long-h" size="small" :disabled="!src" @click="handlerToolbar('scaleX')" />
</Tooltip>
<Tooltip :title="t('component.cropper.btn_scale_y')" placement="bottom">
<a-button type="primary" preIcon="vaadin:arrows-long-v" size="small" :disabled="!src" @click="handlerToolbar('scaleY')" />
</Tooltip>
<Tooltip :title="t('component.cropper.btn_zoom_in')" placement="bottom">
<a-button type="primary" preIcon="ant-design:zoom-in-outlined" size="small" :disabled="!src" @click="handlerToolbar('zoom', 0.1)" />
</Tooltip>
<Tooltip :title="t('component.cropper.btn_zoom_out')" placement="bottom">
<a-button type="primary" preIcon="ant-design:zoom-out-outlined" size="small" :disabled="!src" @click="handlerToolbar('zoom', -0.1)" />
</Tooltip>
</Space>
</div>
</div>
<div :class="`${prefixCls}-right`">
<div :class="`${prefixCls}-preview`">
<img :src="previewSource" v-if="previewSource" :alt="t('component.cropper.preview')" />
</div>
<template v-if="previewSource">
<div :class="`${prefixCls}-group`">
<Avatar :src="previewSource" size="large" />
<Avatar :src="previewSource" :size="48" />
<Avatar :src="previewSource" :size="64" />
<Avatar :src="previewSource" :size="80" />
</div>
</template>
</div>
</div>
</BasicModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { CropendResult, Cropper } from './typing'; import type { CropendResult, Cropper } from './typing';
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import CropperImage from './Cropper.vue'; import CropperImage from './Cropper.vue';
import { Space, Upload, Avatar, Tooltip } from 'ant-design-vue'; import { Space, Upload, Avatar, Tooltip } from 'ant-design-vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import { dataURLtoBlob } from '/@/utils/file/base64Conver'; import { dataURLtoBlob } from '/@/utils/file/base64Conver';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
type apiFunParams = { file: Blob; name: string; filename: string }; type apiFunParams = { file: Blob; name: string; filename: string };
const props = { const props = {
circled: { type: Boolean, default: true }, circled: { type: Boolean, default: true },
uploadApi: { uploadApi: {
type: Function as PropType<(params: apiFunParams) => Promise<any>>, type: Function as PropType<(params: apiFunParams) => Promise<any>>,
}, },
}; };
export default defineComponent({ export default defineComponent({
name: 'CropperModal', name: 'CropperModal',
components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip }, components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip },
props, props,
emits: ['uploadSuccess', 'register'], emits: ['uploadSuccess', 'register'],
setup(props, { emit }) { setup(props, { emit }) {
let filename = ''; let filename = '';
const src = ref(''); const src = ref('');
const previewSource = ref(''); const previewSource = ref('');
const cropper = ref<Cropper>(); const cropper = ref<Cropper>();
let scaleX = 1; let scaleX = 1;
let scaleY = 1; let scaleY = 1;
const { prefixCls } = useDesign('cropper-am'); const { prefixCls } = useDesign('cropper-am');
const [register, { closeModal, setModalProps }] = useModalInner(); const [register, { closeModal, setModalProps }] = useModalInner();
const { t } = useI18n(); const { t } = useI18n();
// Block upload // Block upload
function handleBeforeUpload(file: File) { function handleBeforeUpload(file: File) {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(file); reader.readAsDataURL(file);
src.value = ''; src.value = '';
previewSource.value = ''; previewSource.value = '';
reader.onload = function (e) { reader.onload = function (e) {
src.value = (e.target?.result as string) ?? ''; src.value = (e.target?.result as string) ?? '';
filename = file.name; filename = file.name;
}; };
return false; return false;
} }
function handleCropend({ imgBase64 }: CropendResult) { function handleCropend({ imgBase64 }: CropendResult) {
previewSource.value = imgBase64; previewSource.value = imgBase64;
} }
function handleReady(cropperInstance: Cropper) { function handleReady(cropperInstance: Cropper) {
cropper.value = cropperInstance; cropper.value = cropperInstance;
} }
function handlerToolbar(event: string, arg?: number) { function handlerToolbar(event: string, arg?: number) {
if (event === 'scaleX') { if (event === 'scaleX') {
scaleX = arg = scaleX === -1 ? 1 : -1; scaleX = arg = scaleX === -1 ? 1 : -1;
} }
if (event === 'scaleY') { if (event === 'scaleY') {
scaleY = arg = scaleY === -1 ? 1 : -1; scaleY = arg = scaleY === -1 ? 1 : -1;
} }
cropper?.value?.[event]?.(arg); cropper?.value?.[event]?.(arg);
} }
async function handleOk() { async function handleOk() {
const uploadApi = props.uploadApi; const uploadApi = props.uploadApi;
if (uploadApi && isFunction(uploadApi)) { if (uploadApi && isFunction(uploadApi)) {
const blob = dataURLtoBlob(previewSource.value); const blob = dataURLtoBlob(previewSource.value);
try { try {
setModalProps({ confirmLoading: true }); setModalProps({ confirmLoading: true });
const result = await uploadApi({ name: 'file', file: blob, filename }); const result = await uploadApi({ name: 'file', file: blob, filename });
emit('uploadSuccess', { source: previewSource.value, data: result.data || result.message }); emit('uploadSuccess', { source: previewSource.value, data: result.data || result.message });
closeModal(); closeModal();
} finally { } finally {
setModalProps({ confirmLoading: false }); setModalProps({ confirmLoading: false });
} }
} }
} }
return { return {
t, t,
prefixCls, prefixCls,
src, src,
register, register,
previewSource, previewSource,
handleBeforeUpload, handleBeforeUpload,
handleCropend, handleCropend,
handleReady, handleReady,
handlerToolbar, handlerToolbar,
handleOk, handleOk,
}; };
}, },
}); });
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-cropper-am'; @prefix-cls: ~'@{namespace}-cropper-am';
.@{prefix-cls} { .@{prefix-cls} {
display: flex; display: flex;
&-left, &-left,
&-right { &-right {
height: 340px; height: 340px;
}
&-left {
width: 55%;
}
&-right {
width: 45%;
}
&-cropper {
height: 300px;
background: #eee;
background-image: linear-gradient(
45deg,
rgba(0, 0, 0, 0.25) 25%,
transparent 0,
transparent 75%,
rgba(0, 0, 0, 0.25) 0
),
linear-gradient(
45deg,
rgba(0, 0, 0, 0.25) 25%,
transparent 0,
transparent 75%,
rgba(0, 0, 0, 0.25) 0
);
background-position: 0 0, 12px 12px;
background-size: 24px 24px;
}
&-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
}
&-preview {
width: 220px;
height: 220px;
margin: 0 auto;
overflow: hidden;
border: 1px solid @border-color-base;
border-radius: 50%;
img {
width: 100%;
height: 100%;
}
}
&-group {
display: flex;
padding-top: 8px;
margin-top: 8px;
border-top: 1px solid @border-color-base;
justify-content: space-around;
align-items: center;
}
} }
&-left {
width: 55%;
}
&-right {
width: 45%;
}
&-cropper {
height: 300px;
background: #eee;
background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 0, transparent 75%, rgba(0, 0, 0, 0.25) 0),
linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 0, transparent 75%, rgba(0, 0, 0, 0.25) 0);
background-position: 0 0, 12px 12px;
background-size: 24px 24px;
}
&-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
}
&-preview {
width: 220px;
height: 220px;
margin: 0 auto;
overflow: hidden;
border: 1px solid @border-color-base;
border-radius: 50%;
img {
width: 100%;
height: 100%;
}
}
&-group {
display: flex;
padding-top: 8px;
margin-top: 8px;
border-top: 1px solid @border-color-base;
justify-content: space-around;
align-items: center;
}
}
</style> </style>

View File

@ -1,188 +1,181 @@
<template> <template>
<div :class="getClass" :style="getWrapperStyle"> <div :class="getClass" :style="getWrapperStyle">
<img <img v-show="isReady" ref="imgElRef" :src="src" :alt="alt" :crossorigin="crossorigin" :style="getImageStyle" />
v-show="isReady" </div>
ref="imgElRef"
:src="src"
:alt="alt"
:crossorigin="crossorigin"
:style="getImageStyle"
/>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type {CSSProperties} from 'vue'; import type { CSSProperties } from 'vue';
import {defineComponent, onMounted, ref, unref, computed, onUnmounted} from 'vue'; import { defineComponent, onMounted, ref, unref, computed, onUnmounted } from 'vue';
import Cropper from 'cropperjs'; import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css'; import 'cropperjs/dist/cropper.css';
import {useDesign} from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import {useDebounceFn} from '@vueuse/shared'; import { useDebounceFn } from '@vueuse/shared';
type Options = Cropper.Options; type Options = Cropper.Options;
const defaultOptions: Options = { const defaultOptions: Options = {
aspectRatio: 1, aspectRatio: 1,
zoomable: true, zoomable: true,
zoomOnTouch: true, zoomOnTouch: true,
zoomOnWheel: true, zoomOnWheel: true,
cropBoxMovable: true, cropBoxMovable: true,
cropBoxResizable: true, cropBoxResizable: true,
toggleDragModeOnDblclick: true, toggleDragModeOnDblclick: true,
autoCrop: true, autoCrop: true,
background: true, background: true,
highlight: true, highlight: true,
center: true, center: true,
responsive: true, responsive: true,
restore: true, restore: true,
checkCrossOrigin: true, checkCrossOrigin: true,
checkOrientation: true, checkOrientation: true,
scalable: true, scalable: true,
modal: true, modal: true,
guides: true, guides: true,
movable: true, movable: true,
rotatable: true, rotatable: true,
}; };
const props = { const props = {
src: {type: String, required: true}, src: { type: String, required: true },
alt: {type: String}, alt: { type: String },
circled: {type: Boolean, default: false}, circled: { type: Boolean, default: false },
realTimePreview: {type: Boolean, default: true}, realTimePreview: { type: Boolean, default: true },
height: {type: [String, Number], default: '360px'}, height: { type: [String, Number], default: '360px' },
crossorigin: { crossorigin: {
type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>, type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>,
default: undefined, default: undefined,
}, },
imageStyle: {type: Object as PropType<CSSProperties>, default: () => ({})}, imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
options: {type: Object as PropType<Options>, default: () => ({})}, options: { type: Object as PropType<Options>, default: () => ({}) },
}; };
export default defineComponent({ export default defineComponent({
name: 'CropperImage', name: 'CropperImage',
props, props,
emits: ['cropend', 'ready', 'cropendError'], emits: ['cropend', 'ready', 'cropendError'],
setup(props, {attrs, emit}) { setup(props, { attrs, emit }) {
const imgElRef = ref<ElRef<HTMLImageElement>>(); const imgElRef = ref<ElRef<HTMLImageElement>>();
const cropper = ref<Nullable<Cropper>>(); const cropper = ref<Nullable<Cropper>>();
const isReady = ref(false); const isReady = ref(false);
const {prefixCls} = useDesign('cropper-image'); const { prefixCls } = useDesign('cropper-image');
const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80); const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80);
const getImageStyle = computed((): CSSProperties => { const getImageStyle = computed((): CSSProperties => {
return { return {
height: props.height, height: props.height,
maxWidth: '100%', maxWidth: '100%',
...props.imageStyle, ...props.imageStyle,
}; };
});
const getClass = computed(() => {
return [
prefixCls,
attrs.class,
{
[`${prefixCls}--circled`]: props.circled,
},
];
});
const getWrapperStyle = computed((): CSSProperties => {
return { height: `${props.height}`.replace(/px/, '') + 'px' };
});
onMounted(init);
onUnmounted(() => {
cropper.value?.destroy();
});
async function init() {
const imgEl = unref(imgElRef);
if (!imgEl) {
return;
}
cropper.value = new Cropper(imgEl, {
...defaultOptions,
ready: () => {
isReady.value = true;
realTimeCroppered();
emit('ready', cropper.value);
},
crop() {
debounceRealTimeCroppered();
},
zoom() {
debounceRealTimeCroppered();
},
cropmove() {
debounceRealTimeCroppered();
},
...props.options,
});
}
// Real-time display preview
function realTimeCroppered() {
props.realTimePreview && croppered();
}
// event: return base64 and width and height information after cropping
function croppered() {
if (!cropper.value) {
return;
}
let imgInfo = cropper.value.getData();
const canvas = props.circled ? getRoundedCanvas() : cropper.value.getCroppedCanvas();
canvas.toBlob((blob) => {
if (!blob) {
return;
}
let fileReader: FileReader = new FileReader();
fileReader.readAsDataURL(blob);
fileReader.onloadend = (e) => {
emit('cropend', {
imgBase64: e.target?.result ?? '',
imgInfo,
}); });
};
fileReader.onerror = () => {
emit('cropendError');
};
}, 'image/png');
}
const getClass = computed(() => { // Get a circular picture canvas
return [ function getRoundedCanvas() {
prefixCls, const sourceCanvas = cropper.value!.getCroppedCanvas();
attrs.class, const canvas = document.createElement('canvas');
{ const context = canvas.getContext('2d')!;
[`${prefixCls}--circled`]: props.circled, const width = sourceCanvas.width;
}, const height = sourceCanvas.height;
]; canvas.width = width;
}); canvas.height = height;
context.imageSmoothingEnabled = true;
context.drawImage(sourceCanvas, 0, 0, width, height);
context.globalCompositeOperation = 'destination-in';
context.beginPath();
context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true);
context.fill();
return canvas;
}
const getWrapperStyle = computed((): CSSProperties => { return { getClass, imgElRef, getWrapperStyle, getImageStyle, isReady, croppered };
return {height: `${props.height}`.replace(/px/, '') + 'px'}; },
}); });
onMounted(init);
onUnmounted(() => {
cropper.value?.destroy();
});
async function init() {
const imgEl = unref(imgElRef);
if (!imgEl) {
return;
}
cropper.value = new Cropper(imgEl, {
...defaultOptions,
ready: () => {
isReady.value = true;
realTimeCroppered();
emit('ready', cropper.value);
},
crop() {
debounceRealTimeCroppered();
},
zoom() {
debounceRealTimeCroppered();
},
cropmove() {
debounceRealTimeCroppered();
},
...props.options,
});
}
// Real-time display preview
function realTimeCroppered() {
props.realTimePreview && croppered();
}
// event: return base64 and width and height information after cropping
function croppered() {
if (!cropper.value) {
return;
}
let imgInfo = cropper.value.getData();
const canvas = props.circled ? getRoundedCanvas() : cropper.value.getCroppedCanvas();
canvas.toBlob((blob) => {
if (!blob) {
return;
}
let fileReader: FileReader = new FileReader();
fileReader.readAsDataURL(blob);
fileReader.onloadend = (e) => {
emit('cropend', {
imgBase64: e.target?.result ?? '',
imgInfo,
});
};
fileReader.onerror = () => {
emit('cropendError');
};
}, 'image/png');
}
// Get a circular picture canvas
function getRoundedCanvas() {
const sourceCanvas = cropper.value!.getCroppedCanvas();
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d')!;
const width = sourceCanvas.width;
const height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.imageSmoothingEnabled = true;
context.drawImage(sourceCanvas, 0, 0, width, height);
context.globalCompositeOperation = 'destination-in';
context.beginPath();
context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true);
context.fill();
return canvas;
}
return {getClass, imgElRef, getWrapperStyle, getImageStyle, isReady, croppered};
},
});
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-cropper-image'; @prefix-cls: ~'@{namespace}-cropper-image';
.@{prefix-cls} { .@{prefix-cls} {
&--circled { &--circled {
.cropper-view-box, .cropper-view-box,
.cropper-face { .cropper-face {
border-radius: 50%; border-radius: 50%;
} }
}
} }
}
</style> </style>

View File

@ -2,43 +2,19 @@
<div :class="getClass" :style="getStyle"> <div :class="getClass" :style="getStyle">
<div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal"> <div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal">
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle"> <div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
<Icon <Icon icon="ant-design:cloud-upload-outlined" :size="getIconWidth" :style="getImageWrapperStyle" color="#d6d6d6" />
icon="ant-design:cloud-upload-outlined"
:size="getIconWidth"
:style="getImageWrapperStyle"
color="#d6d6d6"
/>
</div> </div>
<img :src="sourceValue" v-if="sourceValue" alt="avatar" /> <img :src="sourceValue" v-if="sourceValue" alt="avatar" />
</div> </div>
<a-button <a-button :class="`${prefixCls}-upload-btn`" @click="openModal" v-if="showBtn" v-bind="btnProps">
:class="`${prefixCls}-upload-btn`"
@click="openModal"
v-if="showBtn"
v-bind="btnProps"
>
{{ btnText ? btnText : t('component.cropper.selectImage') }} {{ btnText ? btnText : t('component.cropper.selectImage') }}
</a-button> </a-button>
<CopperModal <CopperModal @register="register" @uploadSuccess="handleUploadSuccess" :uploadApi="uploadApi" :src="sourceValue" />
@register="register"
@uploadSuccess="handleUploadSuccess"
:uploadApi="uploadApi"
:src="sourceValue"
/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { import { defineComponent, computed, CSSProperties, unref, ref, watchEffect, watch, PropType } from 'vue';
defineComponent,
computed,
CSSProperties,
unref,
ref,
watchEffect,
watch,
PropType,
} from 'vue';
import CopperModal from './CopperModal.vue'; import CopperModal from './CopperModal.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
@ -76,22 +52,20 @@
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) })); const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }));
const getImageWrapperStyle = computed( const getImageWrapperStyle = computed((): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }));
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) })
);
watchEffect(() => { watchEffect(() => {
sourceValue.value = props.value || ''; sourceValue.value = props.value || '';
}); });
watch( watch(
() => sourceValue.value, () => sourceValue.value,
(v: string) => { (v: string) => {
emit('update:value', v); emit('update:value', v);
} }
); );
function handleUploadSuccess({ source,data }) { function handleUploadSuccess({ source, data }) {
sourceValue.value = source; sourceValue.value = source;
emit('change', source, data); emit('change', source, data);
createMessage.success(t('component.cropper.uploadSuccess')); createMessage.success(t('component.cropper.uploadSuccess'));

View File

@ -12,10 +12,7 @@ export interface DescItem {
span?: number; span?: number;
show?: (...arg: any) => boolean; show?: (...arg: any) => boolean;
// render // render
render?: ( render?: (val: any, data: Recordable) => VNode | undefined | JSX.Element | Element | string | number;
val: any,
data: Recordable
) => VNode | undefined | JSX.Element | Element | string | number;
} }
export interface DescriptionProps extends DescriptionsProps { export interface DescriptionProps extends DescriptionsProps {

View File

@ -1,12 +1,7 @@
<template> <template>
<Drawer :class="prefixCls" @close="onClose" v-bind="getBindValues"> <Drawer :class="prefixCls" @close="onClose" v-bind="getBindValues">
<template #title v-if="!$slots.title"> <template #title v-if="!$slots.title">
<DrawerHeader <DrawerHeader :title="getMergeProps.title" :isDetail="isDetail" :showDetailBack="showDetailBack" @close="onClose">
:title="getMergeProps.title"
:isDetail="isDetail"
:showDetailBack="showDetailBack"
@close="onClose"
>
<template #titleToolbar> <template #titleToolbar>
<slot name="titleToolbar"></slot> <slot name="titleToolbar"></slot>
</template> </template>
@ -16,11 +11,7 @@
<slot name="title"></slot> <slot name="title"></slot>
</template> </template>
<ScrollContainer <ScrollContainer :style="getScrollContentStyle" v-loading="getLoading" :loading-tip="loadingText || t('common.loadingText')">
:style="getScrollContentStyle"
v-loading="getLoading"
:loading-tip="loadingText || t('common.loadingText')"
>
<slot></slot> <slot></slot>
</ScrollContainer> </ScrollContainer>
<DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight"> <DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight">
@ -33,16 +24,7 @@
<script lang="ts"> <script lang="ts">
import type { DrawerInstance, DrawerProps } from './typing'; import type { DrawerInstance, DrawerProps } from './typing';
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import { import { defineComponent, ref, computed, watch, unref, nextTick, toRaw, getCurrentInstance } from 'vue';
defineComponent,
ref,
computed,
watch,
unref,
nextTick,
toRaw,
getCurrentInstance,
} from 'vue';
import { Drawer } from 'ant-design-vue'; import { Drawer } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { isFunction, isNumber } from '/@/utils/is'; import { isFunction, isNumber } from '/@/utils/is';
@ -115,9 +97,7 @@
const getFooterHeight = computed(() => { const getFooterHeight = computed(() => {
const { footerHeight, showFooter } = unref(getProps); const { footerHeight, showFooter } = unref(getProps);
if (showFooter && footerHeight) { if (showFooter && footerHeight) {
return isNumber(footerHeight) return isNumber(footerHeight) ? `${footerHeight}px` : `${footerHeight.replace('px', '')}px`;
? `${footerHeight}px`
: `${footerHeight.replace('px', '')}px`;
} }
return `0px`; return `0px`;
}); });
@ -135,21 +115,21 @@
}); });
watch( watch(
() => props.visible, () => props.visible,
(newVal, oldVal) => { (newVal, oldVal) => {
if (newVal !== oldVal) visibleRef.value = newVal; if (newVal !== oldVal) visibleRef.value = newVal;
}, },
{ deep: true } { deep: true }
); );
watch( watch(
() => visibleRef.value, () => visibleRef.value,
(visible) => { (visible) => {
nextTick(() => { nextTick(() => {
emit('visible-change', visible); emit('visible-change', visible);
instance && drawerInstance.emitVisible?.(visible, instance.uid); instance && drawerInstance.emitVisible?.(visible, instance.uid);
}); });
} }
); );
// Cancel event // Cancel event

View File

@ -6,14 +6,7 @@
{{ cancelText }} {{ cancelText }}
</a-button> </a-button>
<slot name="centerFooter"></slot> <slot name="centerFooter"></slot>
<a-button <a-button :type="okType" @click="handleOk" v-bind="okButtonProps" class="mr-2" :loading="confirmLoading" v-if="showOkBtn">
:type="okType"
@click="handleOk"
v-bind="okButtonProps"
class="mr-2"
:loading="confirmLoading"
v-if="showOkBtn"
>
{{ okText }} {{ okText }}
</a-button> </a-button>
<slot name="appendFooter"></slot> <slot name="appendFooter"></slot>

View File

@ -1,20 +1,5 @@
import type { import type { UseDrawerReturnType, DrawerInstance, ReturnMethods, DrawerProps, UseDrawerInnerReturnType } from './typing';
UseDrawerReturnType, import { ref, getCurrentInstance, unref, reactive, watchEffect, nextTick, toRaw, computed } from 'vue';
DrawerInstance,
ReturnMethods,
DrawerProps,
UseDrawerInnerReturnType,
} from './typing';
import {
ref,
getCurrentInstance,
unref,
reactive,
watchEffect,
nextTick,
toRaw,
computed,
} from 'vue';
import { isProdMode } from '/@/utils/env'; import { isProdMode } from '/@/utils/env';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { tryOnUnmounted } from '@vueuse/core'; import { tryOnUnmounted } from '@vueuse/core';

View File

@ -6,16 +6,8 @@
<template #overlay> <template #overlay>
<a-menu :class="[`${prefixCls}-menu`]" :selectedKeys="selectedKeys"> <a-menu :class="[`${prefixCls}-menu`]" :selectedKeys="selectedKeys">
<template v-for="item in dropMenuList" :key="`${item.event}`"> <template v-for="item in dropMenuList" :key="`${item.event}`">
<a-menu-item <a-menu-item v-bind="getAttr(item.event)" @click="handleClickMenu(item)" :disabled="item.disabled" :class="[{ 'is-pop-confirm': item.popConfirm }]">
v-bind="getAttr(item.event)" <a-popconfirm v-if="popconfirm && item.popConfirm" v-bind="getPopConfirmAttrs(item.popConfirm)">
@click="handleClickMenu(item)"
:disabled="item.disabled"
:class="[{'is-pop-confirm': item.popConfirm}]"
>
<a-popconfirm
v-if="popconfirm && item.popConfirm"
v-bind="getPopConfirmAttrs(item.popConfirm)"
>
<template #icon v-if="item.popConfirm.icon"> <template #icon v-if="item.popConfirm.icon">
<Icon :icon="item.popConfirm.icon" /> <Icon :icon="item.popConfirm.icon" />
</template> </template>
@ -43,7 +35,7 @@
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import {useDesign} from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign';
const ADropdown = Dropdown; const ADropdown = Dropdown;
const AMenu = Menu; const AMenu = Menu;
@ -87,10 +79,8 @@
const getPopConfirmAttrs = computed(() => { const getPopConfirmAttrs = computed(() => {
return (attrs) => { return (attrs) => {
const originAttrs = omit(attrs, ['confirm', 'cancel', 'icon']); const originAttrs = omit(attrs, ['confirm', 'cancel', 'icon']);
if (!attrs.onConfirm && attrs.confirm && isFunction(attrs.confirm)) if (!attrs.onConfirm && attrs.confirm && isFunction(attrs.confirm)) originAttrs['onConfirm'] = attrs.confirm;
originAttrs['onConfirm'] = attrs.confirm; if (!attrs.onCancel && attrs.cancel && isFunction(attrs.cancel)) originAttrs['onCancel'] = attrs.cancel;
if (!attrs.onCancel && attrs.cancel && isFunction(attrs.cancel))
originAttrs['onCancel'] = attrs.cancel;
return originAttrs; return originAttrs;
}; };
}); });
@ -99,19 +89,17 @@
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-dropdown'; @prefix-cls: ~'@{namespace}-basic-dropdown';
.@{prefix-cls} { .@{prefix-cls} {
// update-begin--author:sunjianlei---date:20220322---for: VUEN-180
&-menu .ant-dropdown-menu-item.is-pop-confirm {
padding: 0;
// update-begin--author:sunjianlei---date:20220322---for: VUEN-180 .dropdown-event-area {
&-menu .ant-dropdown-menu-item.is-pop-confirm { padding: 5px 12px;
padding: 0; }
.dropdown-event-area {
padding: 5px 12px;
} }
// update-end--author:sunjianlei---date:20220322---for: VUEN-180
} }
// update-end--author:sunjianlei---date:20220322---for: VUEN-180 </style>
}
</style>

View File

@ -6,13 +6,7 @@ const { utils, writeFile } = xlsx;
const DEF_FILE_NAME = 'excel-list.xlsx'; const DEF_FILE_NAME = 'excel-list.xlsx';
export function jsonToSheetXlsx<T = any>({ export function jsonToSheetXlsx<T = any>({ data, header, filename = DEF_FILE_NAME, json2sheetOpts = {}, write2excelOpts = { bookType: 'xlsx' } }: JsonToSheet<T>) {
data,
header,
filename = DEF_FILE_NAME,
json2sheetOpts = {},
write2excelOpts = { bookType: 'xlsx' },
}: JsonToSheet<T>) {
const arrData = [...data]; const arrData = [...data];
if (header) { if (header) {
arrData.unshift(header); arrData.unshift(header);
@ -33,12 +27,7 @@ export function jsonToSheetXlsx<T = any>({
/* at this point, out.xlsb will have been downloaded */ /* at this point, out.xlsb will have been downloaded */
} }
export function aoaToSheetXlsx<T = any>({ export function aoaToSheetXlsx<T = any>({ data, header, filename = DEF_FILE_NAME, write2excelOpts = { bookType: 'xlsx' } }: AoAToSheet<T>) {
data,
header,
filename = DEF_FILE_NAME,
write2excelOpts = { bookType: 'xlsx' },
}: AoAToSheet<T>) {
const arrData = [...data]; const arrData = [...data];
if (header) { if (header) {
arrData.unshift(header); arrData.unshift(header);

View File

@ -1,16 +1,6 @@
<template> <template>
<BasicModal <BasicModal v-bind="$attrs" :title="t('component.excel.exportModalTitle')" @ok="handleOk" @register="registerModal">
v-bind="$attrs" <BasicForm :labelWidth="100" :schemas="schemas" :showActionButtonGroup="false" @register="registerForm" />
:title="t('component.excel.exportModalTitle')"
@ok="handleOk"
@register="registerModal"
>
<BasicForm
:labelWidth="100"
:schemas="schemas"
:showActionButtonGroup="false"
@register="registerForm"
/>
</BasicModal> </BasicModal>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@ -1,12 +1,6 @@
<template> <template>
<div> <div>
<input <input ref="inputRef" type="file" v-show="false" accept=".xlsx, .xls" @change="handleInputClick" />
ref="inputRef"
type="file"
v-show="false"
accept=".xlsx, .xls"
@change="handleInputClick"
/>
<div @click="handleUpload"> <div @click="handleUpload">
<slot></slot> <slot></slot>
</div> </div>

View File

@ -19,7 +19,7 @@ export { default as JCategorySelect } from './src/jeecg/components/JCategorySele
export { default as JSelectMultiple } from './src/jeecg/components/JSelectMultiple.vue'; export { default as JSelectMultiple } from './src/jeecg/components/JSelectMultiple.vue';
export { default as JPopup } from './src/jeecg/components/JPopup.vue'; export { default as JPopup } from './src/jeecg/components/JPopup.vue';
export { default as JAreaSelect } from './src/jeecg/components/JAreaSelect.vue'; export { default as JAreaSelect } from './src/jeecg/components/JAreaSelect.vue';
export { JEasyCron, JEasyCronInner, JEasyCronModal } from '/@/components/Form/src/jeecg/components/JEasyCron' export { JEasyCron, JEasyCronInner, JEasyCronModal } from '/@/components/Form/src/jeecg/components/JEasyCron';
export { default as JCheckbox } from './src/jeecg/components/JCheckbox.vue'; export { default as JCheckbox } from './src/jeecg/components/JCheckbox.vue';
export { default as JInput } from './src/jeecg/components/JInput.vue'; export { default as JInput } from './src/jeecg/components/JInput.vue';
export { default as JEllipsis } from './src/jeecg/components/JEllipsis.vue'; export { default as JEllipsis } from './src/jeecg/components/JEllipsis.vue';
@ -30,6 +30,6 @@ export { default as JSelectUserByDept } from './src/jeecg/components/JSelectUser
export { default as JEditor } from './src/jeecg/components/JEditor.vue'; export { default as JEditor } from './src/jeecg/components/JEditor.vue';
export { default as JImageUpload } from './src/jeecg/components/JImageUpload.vue'; export { default as JImageUpload } from './src/jeecg/components/JImageUpload.vue';
// Jeecg自定义校验 // Jeecg自定义校验
export { JCronValidator } from '/@/components/Form/src/jeecg/components/JEasyCron' export { JCronValidator } from '/@/components/Form/src/jeecg/components/JEasyCron';
export { BasicForm }; export { BasicForm };

View File

@ -1,324 +1,316 @@
<template> <template>
<Form v-bind="getBindValue" :class="getFormClass" ref="formElRef" :model="formModel" @keypress.enter="handleEnterPress"> <Form v-bind="getBindValue" :class="getFormClass" ref="formElRef" :model="formModel" @keypress.enter="handleEnterPress">
<Row v-bind="getRow"> <Row v-bind="getRow">
<slot name="formHeader"></slot> <slot name="formHeader"></slot>
<template v-for="schema in getSchema" :key="schema.field"> <template v-for="schema in getSchema" :key="schema.field">
<FormItem :tableAction="tableAction" :formActionType="formActionType" :schema="schema" :formProps="getProps" :allDefaultValues="defaultValueRef" :formModel="formModel" :setFormModel="setFormModel"> <FormItem
<template #[item]="data" v-for="item in Object.keys($slots)"> :tableAction="tableAction"
<slot :name="item" v-bind="data || {}"></slot> :formActionType="formActionType"
</template> :schema="schema"
</FormItem> :formProps="getProps"
</template> :allDefaultValues="defaultValueRef"
:formModel="formModel"
:setFormModel="setFormModel"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</FormItem>
</template>
<FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced"> <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
<template #[item]="data" v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"> <template #[item]="data" v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']">
<slot :name="item" v-bind="data || {}"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</FormAction> </FormAction>
<slot name="formFooter"></slot> <slot name="formFooter"></slot>
</Row> </Row>
</Form> </Form>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { FormActionType, FormProps, FormSchema } from './types/form'; import type { FormActionType, FormProps, FormSchema } from './types/form';
import type { AdvanceState } from './types/hooks'; import type { AdvanceState } from './types/hooks';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue'; import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
import { Form, Row } from 'ant-design-vue'; import { Form, Row } from 'ant-design-vue';
import FormItem from './components/FormItem.vue'; import FormItem from './components/FormItem.vue';
import FormAction from './components/FormAction.vue'; import FormAction from './components/FormAction.vue';
import { dateItemType } from './helper'; import { dateItemType } from './helper';
import { dateUtil } from '/@/utils/dateUtil'; import { dateUtil } from '/@/utils/dateUtil';
// import { cloneDeep } from 'lodash-es'; // import { cloneDeep } from 'lodash-es';
import { deepMerge } from '/@/utils'; import { deepMerge } from '/@/utils';
import { useFormValues } from './hooks/useFormValues'; import { useFormValues } from './hooks/useFormValues';
import useAdvanced from './hooks/useAdvanced'; import useAdvanced from './hooks/useAdvanced';
import { useFormEvents } from './hooks/useFormEvents'; import { useFormEvents } from './hooks/useFormEvents';
import { createFormContext } from './hooks/useFormContext'; import { createFormContext } from './hooks/useFormContext';
import { useAutoFocus } from './hooks/useAutoFocus'; import { useAutoFocus } from './hooks/useAutoFocus';
import { useModalContext } from '/@/components/Modal'; import { useModalContext } from '/@/components/Modal';
import { basicProps } from './props'; import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({ export default defineComponent({
name: 'BasicForm', name: 'BasicForm',
components: { FormItem, Form, Row, FormAction }, components: { FormItem, Form, Row, FormAction },
props: basicProps, props: basicProps,
emits: ['advanced-change', 'reset', 'submit', 'register'], emits: ['advanced-change', 'reset', 'submit', 'register'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
const formModel = reactive<Recordable>({}); const formModel = reactive<Recordable>({});
const modalFn = useModalContext(); const modalFn = useModalContext();
const advanceState = reactive<AdvanceState>({ const advanceState = reactive<AdvanceState>({
// //
isAdvanced: false, isAdvanced: false,
hideAdvanceBtn: true, hideAdvanceBtn: true,
isLoad: false, isLoad: false,
actionSpan: 6, actionSpan: 6,
}); });
const defaultValueRef = ref<Recordable>({}); const defaultValueRef = ref<Recordable>({});
const isInitedDefaultRef = ref(false); const isInitedDefaultRef = ref(false);
const propsRef = ref<Partial<FormProps>>({}); const propsRef = ref<Partial<FormProps>>({});
const schemaRef = ref<Nullable<FormSchema[]>>(null); const schemaRef = ref<Nullable<FormSchema[]>>(null);
const formElRef = ref<Nullable<FormActionType>>(null); const formElRef = ref<Nullable<FormActionType>>(null);
const { prefixCls } = useDesign('basic-form'); const { prefixCls } = useDesign('basic-form');
// Get the basic configuration of the form // Get the basic configuration of the form
const getProps = computed((): FormProps => { const getProps = computed((): FormProps => {
return { ...props, ...unref(propsRef) } as FormProps; return { ...props, ...unref(propsRef) } as FormProps;
}); });
const getFormClass = computed(() => { const getFormClass = computed(() => {
return [ return [
prefixCls, prefixCls,
{ {
[`${prefixCls}--compact`]: unref(getProps).compact, [`${prefixCls}--compact`]: unref(getProps).compact,
}, },
]; ];
}); });
// Get uniform row style and Row configuration for the entire form // Get uniform row style and Row configuration for the entire form
const getRow = computed((): Recordable => { const getRow = computed((): Recordable => {
const { baseRowStyle = {}, rowProps } = unref(getProps); const { baseRowStyle = {}, rowProps } = unref(getProps);
return { return {
style: baseRowStyle, style: baseRowStyle,
...rowProps, ...rowProps,
}; };
}); });
const getBindValue = computed( const getBindValue = computed(() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable));
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable)
);
const getSchema = computed((): FormSchema[] => { const getSchema = computed((): FormSchema[] => {
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
for (const schema of schemas) { for (const schema of schemas) {
const { defaultValue, component } = schema; const { defaultValue, component } = schema;
// handle date type // handle date type
if (defaultValue && dateItemType.includes(component)) { if (defaultValue && dateItemType.includes(component)) {
if (!Array.isArray(defaultValue)) { if (!Array.isArray(defaultValue)) {
schema.defaultValue = dateUtil(defaultValue); schema.defaultValue = dateUtil(defaultValue);
} else { } else {
const def: moment.Moment[] = []; const def: moment.Moment[] = [];
defaultValue.forEach((item) => { defaultValue.forEach((item) => {
def.push(dateUtil(item)); def.push(dateUtil(item));
}); });
schema.defaultValue = def; schema.defaultValue = def;
}
}
}
if (unref(getProps).showAdvancedButton) {
return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[];
} else {
return schemas as FormSchema[];
}
});
const { handleToggleAdvanced } = useAdvanced({
advanceState,
emit,
getProps,
getSchema,
formModel,
defaultValueRef,
});
const { handleFormValues, initDefault } = useFormValues({
getProps,
defaultValueRef,
getSchema,
formModel,
});
useAutoFocus({
getSchema,
getProps,
isInitedDefault: isInitedDefaultRef,
formElRef: formElRef as Ref<FormActionType>,
});
const {
handleSubmit,
setFieldsValue,
clearValidate,
validate,
validateFields,
getFieldsValue,
updateSchema,
resetSchema,
appendSchemaByField,
removeSchemaByFiled,
resetFields,
scrollToField,
} = useFormEvents({
emit,
getProps,
formModel,
getSchema,
defaultValueRef,
formElRef: formElRef as Ref<FormActionType>,
schemaRef: schemaRef as Ref<FormSchema[]>,
handleFormValues,
});
createFormContext({
resetAction: resetFields,
submitAction: handleSubmit,
});
watch(
() => unref(getProps).model,
() => {
const { model } = unref(getProps);
if (!model) return;
setFieldsValue(model);
},
{
immediate: true,
}
);
watch(
() => unref(getProps).schemas,
(schemas) => {
resetSchema(schemas ?? []);
}
);
watch(
() => getSchema.value,
(schema) => {
nextTick(() => {
// Solve the problem of modal adaptive height calculation when the form is placed in the modal
modalFn?.redoModalHeight?.();
});
if (unref(isInitedDefaultRef)) {
return;
}
if (schema?.length) {
initDefault();
isInitedDefaultRef.value = true;
}
}
);
async function setProps(formProps: Partial<FormProps>): Promise<void> {
propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
} }
}
}
if (unref(getProps).showAdvancedButton) {
return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[];
} else {
return schemas as FormSchema[];
}
});
function setFormModel(key: string, value: any) { const { handleToggleAdvanced } = useAdvanced({
formModel[key] = value; advanceState,
const { validateTrigger } = unref(getBindValue); emit,
if (!validateTrigger || validateTrigger === 'change') { getProps,
validateFields([key]).catch((_) => {}); getSchema,
} formModel,
} defaultValueRef,
});
function handleEnterPress(e: KeyboardEvent) { const { handleFormValues, initDefault } = useFormValues({
const { autoSubmitOnEnter } = unref(getProps); getProps,
if (!autoSubmitOnEnter) return; defaultValueRef,
if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) { getSchema,
const target: HTMLElement = e.target as HTMLElement; formModel,
if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') { });
handleSubmit();
}
}
}
const formActionType: Partial<FormActionType> = { useAutoFocus({
getFieldsValue, getSchema,
setFieldsValue, getProps,
resetFields, isInitedDefault: isInitedDefaultRef,
updateSchema, formElRef: formElRef as Ref<FormActionType>,
resetSchema, });
setProps,
removeSchemaByFiled,
appendSchemaByField,
clearValidate,
validateFields,
validate,
submit: handleSubmit,
scrollToField: scrollToField,
};
onMounted(() => { const { handleSubmit, setFieldsValue, clearValidate, validate, validateFields, getFieldsValue, updateSchema, resetSchema, appendSchemaByField, removeSchemaByFiled, resetFields, scrollToField } =
initDefault(); useFormEvents({
emit('register', formActionType); emit,
}); getProps,
formModel,
getSchema,
defaultValueRef,
formElRef: formElRef as Ref<FormActionType>,
schemaRef: schemaRef as Ref<FormSchema[]>,
handleFormValues,
});
return { createFormContext({
getBindValue, resetAction: resetFields,
handleToggleAdvanced, submitAction: handleSubmit,
handleEnterPress, });
formModel,
defaultValueRef, watch(
advanceState, () => unref(getProps).model,
getRow, () => {
getProps, const { model } = unref(getProps);
formElRef, if (!model) return;
getSchema, setFieldsValue(model);
formActionType: formActionType as any,
setFormModel,
getFormClass,
getFormActionBindProps: computed(
(): Recordable => ({ ...getProps.value, ...advanceState })
),
...formActionType,
};
}, },
}); {
immediate: true,
}
);
watch(
() => unref(getProps).schemas,
(schemas) => {
resetSchema(schemas ?? []);
}
);
watch(
() => getSchema.value,
(schema) => {
nextTick(() => {
// Solve the problem of modal adaptive height calculation when the form is placed in the modal
modalFn?.redoModalHeight?.();
});
if (unref(isInitedDefaultRef)) {
return;
}
if (schema?.length) {
initDefault();
isInitedDefaultRef.value = true;
}
}
);
async function setProps(formProps: Partial<FormProps>): Promise<void> {
propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
}
function setFormModel(key: string, value: any) {
formModel[key] = value;
const { validateTrigger } = unref(getBindValue);
if (!validateTrigger || validateTrigger === 'change') {
validateFields([key]).catch((_) => {});
}
}
function handleEnterPress(e: KeyboardEvent) {
const { autoSubmitOnEnter } = unref(getProps);
if (!autoSubmitOnEnter) return;
if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
const target: HTMLElement = e.target as HTMLElement;
if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
handleSubmit();
}
}
}
const formActionType: Partial<FormActionType> = {
getFieldsValue,
setFieldsValue,
resetFields,
updateSchema,
resetSchema,
setProps,
removeSchemaByFiled,
appendSchemaByField,
clearValidate,
validateFields,
validate,
submit: handleSubmit,
scrollToField: scrollToField,
};
onMounted(() => {
initDefault();
emit('register', formActionType);
});
return {
getBindValue,
handleToggleAdvanced,
handleEnterPress,
formModel,
defaultValueRef,
advanceState,
getRow,
getProps,
formElRef,
getSchema,
formActionType: formActionType as any,
setFormModel,
getFormClass,
getFormActionBindProps: computed((): Recordable => ({ ...getProps.value, ...advanceState })),
...formActionType,
};
},
});
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-form'; @prefix-cls: ~'@{namespace}-basic-form';
.@{prefix-cls} { .@{prefix-cls} {
.ant-form-item { .ant-form-item {
&-label label::after { &-label label::after {
margin: 0 6px 0 2px; margin: 0 6px 0 2px;
} }
&-with-help { &-with-help {
margin-bottom: 0; margin-bottom: 0;
} }
&:not(.ant-form-item-with-help) { &:not(.ant-form-item-with-help) {
margin-bottom: 20px; margin-bottom: 20px;
} }
&.suffix-item { &.suffix-item {
.ant-form-item-children { .ant-form-item-children {
display: flex; display: flex;
}
.ant-form-item-control {
margin-top: 4px;
}
.suffix {
display: inline-flex;
padding-left: 6px;
margin-top: 1px;
line-height: 1;
align-items: center;
}
}
} }
.ant-form-explain { .ant-form-item-control {
font-size: 14px; margin-top: 4px;
} }
&--compact { .suffix {
.ant-form-item { display: inline-flex;
margin-bottom: 8px !important; padding-left: 6px;
} margin-top: 1px;
line-height: 1;
align-items: center;
} }
}
} }
.ant-form-explain {
font-size: 14px;
}
&--compact {
.ant-form-item {
margin-bottom: 8px !important;
}
}
}
</style> </style>

View File

@ -4,22 +4,7 @@ import type { ComponentType } from './types/index';
/** /**
* Component list, register here to setting it in the form * Component list, register here to setting it in the form
*/ */
import { import { Input, Select, Radio, Checkbox, AutoComplete, Cascader, DatePicker, InputNumber, Switch, TimePicker, TreeSelect, Slider, Rate, Divider } from 'ant-design-vue';
Input,
Select,
Radio,
Checkbox,
AutoComplete,
Cascader,
DatePicker,
InputNumber,
Switch,
TimePicker,
TreeSelect,
Slider,
Rate,
Divider,
} from 'ant-design-vue';
import ApiRadioGroup from './components/ApiRadioGroup.vue'; import ApiRadioGroup from './components/ApiRadioGroup.vue';
import RadioButtonGroup from './components/RadioButtonGroup.vue'; import RadioButtonGroup from './components/RadioButtonGroup.vue';
import ApiSelect from './components/ApiSelect.vue'; import ApiSelect from './components/ApiSelect.vue';
@ -47,18 +32,18 @@ import JPopup from './jeecg/components/JPopup.vue';
import JSwitch from './jeecg/components/JSwitch.vue'; import JSwitch from './jeecg/components/JSwitch.vue';
import JTreeDict from './jeecg/components/JTreeDict.vue'; import JTreeDict from './jeecg/components/JTreeDict.vue';
import JInputPop from './jeecg/components/JInputPop.vue'; import JInputPop from './jeecg/components/JInputPop.vue';
import { JEasyCron } from './jeecg/components/JEasyCron' import { JEasyCron } from './jeecg/components/JEasyCron';
import JCheckbox from './jeecg/components/JCheckbox.vue'; import JCheckbox from './jeecg/components/JCheckbox.vue';
import JInput from './jeecg/components/JInput.vue'; import JInput from './jeecg/components/JInput.vue';
import JTreeSelect from './jeecg/components/JTreeSelect.vue'; import JTreeSelect from './jeecg/components/JTreeSelect.vue';
import JEllipsis from './jeecg/components/JEllipsis.vue'; import JEllipsis from './jeecg/components/JEllipsis.vue';
import JSelectUserByDept from './jeecg/components/JSelectUserByDept.vue'; import JSelectUserByDept from './jeecg/components/JSelectUserByDept.vue';
import JUpload from './jeecg/components/JUpload/JUpload.vue' import JUpload from './jeecg/components/JUpload/JUpload.vue';
import JSearchSelect from './jeecg/components/JSearchSelect.vue' import JSearchSelect from './jeecg/components/JSearchSelect.vue';
import JAddInput from './jeecg/components/JAddInput.vue' import JAddInput from './jeecg/components/JAddInput.vue';
import {Time} from '/@/components/Time'; import { Time } from '/@/components/Time';
import JOnlineSelectCascade from './jeecg/components/JOnlineSelectCascade.vue' import JOnlineSelectCascade from './jeecg/components/JOnlineSelectCascade.vue';
import JRangeNumber from './jeecg/components/JRangeNumber.vue' import JRangeNumber from './jeecg/components/JRangeNumber.vue';
const componentMap = new Map<ComponentType, Component>(); const componentMap = new Map<ComponentType, Component>();
@ -125,8 +110,8 @@ componentMap.set('JSelectUserByDept', JSelectUserByDept);
componentMap.set('JUpload', JUpload); componentMap.set('JUpload', JUpload);
componentMap.set('JSearchSelect', JSearchSelect); componentMap.set('JSearchSelect', JSearchSelect);
componentMap.set('JAddInput', JAddInput); componentMap.set('JAddInput', JAddInput);
componentMap.set('JOnlineSelectCascade', JOnlineSelectCascade) componentMap.set('JOnlineSelectCascade', JOnlineSelectCascade);
componentMap.set('JRangeNumber', JRangeNumber) componentMap.set('JRangeNumber', JRangeNumber);
export function add(compName: ComponentType, component: Component) { export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component); componentMap.set(compName, component);

View File

@ -90,7 +90,7 @@
() => { () => {
!unref(isFirstLoad) && fetch(); !unref(isFirstLoad) && fetch();
}, },
{ deep: true }, { deep: true }
); );
async function fetch() { async function fetch() {

View File

@ -1,144 +1,138 @@
<template> <template>
<Select <Select @dropdownVisibleChange="handleFetch" v-bind="$attrs" @change="handleChange" :options="getOptions" v-model:value="state">
@dropdownVisibleChange="handleFetch" <template #[item]="data" v-for="item in Object.keys($slots)">
v-bind="$attrs" <slot :name="item" v-bind="data || {}"></slot>
@change="handleChange" </template>
:options="getOptions" <template #suffixIcon v-if="loading">
v-model:value="state" <LoadingOutlined spin />
> </template>
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #notFoundContent v-if="loading">
<slot :name="item" v-bind="data || {}"></slot>
</template>
<template #suffixIcon v-if="loading">
<LoadingOutlined spin />
</template>
<template #notFoundContent v-if="loading">
<span> <span>
<LoadingOutlined spin class="mr-1" /> <LoadingOutlined spin class="mr-1" />
{{ t('component.form.apiSelectNotFound') }} {{ t('component.form.apiSelectNotFound') }}
</span> </span>
</template> </template>
</Select> </Select>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, ref, watchEffect, computed, unref, watch } from 'vue'; import { defineComponent, PropType, ref, watchEffect, computed, unref, watch } from 'vue';
import { Select } from 'ant-design-vue'; import { Select } from 'ant-design-vue';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { useRuleFormItem } from '/@/hooks/component/useFormItem'; import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import { get, omit } from 'lodash-es'; import { get, omit } from 'lodash-es';
import { LoadingOutlined } from '@ant-design/icons-vue'; import { LoadingOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
type OptionsItem = { label: string; value: string; disabled?: boolean }; type OptionsItem = { label: string; value: string; disabled?: boolean };
export default defineComponent({ export default defineComponent({
name: 'ApiSelect', name: 'ApiSelect',
components: { components: {
Select, Select,
LoadingOutlined, LoadingOutlined,
}, },
inheritAttrs: false, inheritAttrs: false,
props: { props: {
value: [Array, Object, String, Number], value: [Array, Object, String, Number],
numberToString: propTypes.bool, numberToString: propTypes.bool,
api: { api: {
type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>, type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
default: null, default: null,
}, },
// api params // api params
params: { params: {
type: Object as PropType<Recordable>, type: Object as PropType<Recordable>,
default: () => ({}), default: () => ({}),
}, },
// support xxx.xxx.xx // support xxx.xxx.xx
resultField: propTypes.string.def(''), resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'), labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'), valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true), immediate: propTypes.bool.def(true),
}, },
emits: ['options-change', 'change'], emits: ['options-change', 'change'],
setup(props, { emit }) { setup(props, { emit }) {
const options = ref<OptionsItem[]>([]); const options = ref<OptionsItem[]>([]);
const loading = ref(false); const loading = ref(false);
const isFirstLoad = ref(true); const isFirstLoad = ref(true);
const emitData = ref<any[]>([]); const emitData = ref<any[]>([]);
const attrs = useAttrs(); const attrs = useAttrs();
const { t } = useI18n(); const { t } = useI18n();
// Embedded in the form, just use the hook binding to perform form verification // Embedded in the form, just use the hook binding to perform form verification
const [state, setState] = useRuleFormItem(props, 'value', 'change', emitData); const [state, setState] = useRuleFormItem(props, 'value', 'change', emitData);
const getOptions = computed(() => { const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props; const { labelField, valueField, numberToString } = props;
return unref(options).reduce((prev, next: Recordable) => { return unref(options).reduce((prev, next: Recordable) => {
if (next) { if (next) {
const value = next[valueField]; const value = next[valueField];
prev.push({ prev.push({
...omit(next, [labelField, valueField]), ...omit(next, [labelField, valueField]),
label: next[labelField], label: next[labelField],
value: numberToString ? `${value}` : value, value: numberToString ? `${value}` : value,
});
}
return prev;
}, [] as OptionsItem[]);
}); });
}
return prev;
}, [] as OptionsItem[]);
});
watchEffect(() => { watchEffect(() => {
props.immediate && fetch(); props.immediate && fetch();
}); });
watch( watch(
() => props.params, () => props.params,
() => { () => {
!unref(isFirstLoad) && fetch(); !unref(isFirstLoad) && fetch();
},
{ deep: true }
);
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) return;
options.value = [];
try {
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
//--@updateBy-begin----author:liusq---date:20210914------for:multiplevalue------
unref(attrs).mode == 'multiple' && !Array.isArray(unref(state)) && setState([])
//--@updateBy-end----author:liusq---date:20210914------for:multiplevalue------
}
}
async function handleFetch() {
if (!props.immediate && unref(isFirstLoad)) {
await fetch();
isFirstLoad.value = false;
}
}
function emitChange() {
emit('options-change', unref(getOptions));
}
function handleChange(_, ...args) {
emitData.value = args;
}
return { state, attrs, getOptions, loading, t, handleFetch, handleChange };
}, },
}); { deep: true }
);
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) return;
options.value = [];
try {
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
//--@updateBy-begin----author:liusq---date:20210914------for:multiplevalue------
unref(attrs).mode == 'multiple' && !Array.isArray(unref(state)) && setState([]);
//--@updateBy-end----author:liusq---date:20210914------for:multiplevalue------
}
}
async function handleFetch() {
if (!props.immediate && unref(isFirstLoad)) {
await fetch();
isFirstLoad.value = false;
}
}
function emitChange() {
emit('options-change', unref(getOptions));
}
function handleChange(_, ...args) {
emitData.value = args;
}
return { state, attrs, getOptions, loading, t, handleFetch, handleChange };
},
});
</script> </script>

View File

@ -1,86 +1,86 @@
<template> <template>
<a-tree-select v-bind="getAttrs" @change="handleChange"> <a-tree-select v-bind="getAttrs" @change="handleChange">
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
<template #suffixIcon v-if="loading"> <template #suffixIcon v-if="loading">
<LoadingOutlined spin /> <LoadingOutlined spin />
</template> </template>
</a-tree-select> </a-tree-select>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, watch, ref, onMounted, unref } from 'vue'; import { computed, defineComponent, watch, ref, onMounted, unref } from 'vue';
import { TreeSelect } from 'ant-design-vue'; import { TreeSelect } from 'ant-design-vue';
import { isArray, isFunction } from '/@/utils/is'; import { isArray, isFunction } from '/@/utils/is';
import { get } from 'lodash-es'; import { get } from 'lodash-es';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { LoadingOutlined } from '@ant-design/icons-vue'; import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({ export default defineComponent({
name: 'ApiTreeSelect', name: 'ApiTreeSelect',
components: { ATreeSelect: TreeSelect, LoadingOutlined }, components: { ATreeSelect: TreeSelect, LoadingOutlined },
props: { props: {
api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> }, api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
params: { type: Object }, params: { type: Object },
immediate: { type: Boolean, default: true }, immediate: { type: Boolean, default: true },
resultField: propTypes.string.def(''), resultField: propTypes.string.def(''),
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {
const treeData = ref<Recordable[]>([]);
const isFirstLoaded = ref<Boolean>(false);
const loading = ref(false);
const getAttrs = computed(() => {
return {
...(props.api ? { treeData: unref(treeData) } : {}),
...attrs,
};
});
function handleChange(...args) {
emit('change', ...args);
}
watch(
() => props.params,
() => {
!unref(isFirstLoaded) && fetch();
}, },
emits: ['options-change', 'change'], { deep: true }
setup(props, { attrs, emit }) { );
const treeData = ref<Recordable[]>([]);
const isFirstLoaded = ref<Boolean>(false);
const loading = ref(false);
const getAttrs = computed(() => {
return {
...(props.api ? { treeData: unref(treeData) } : {}),
...attrs,
};
});
function handleChange(...args) { watch(
emit('change', ...args); () => props.immediate,
} (v) => {
v && !isFirstLoaded.value && fetch();
}
);
watch( onMounted(() => {
() => props.params, props.immediate && fetch();
() => { });
!unref(isFirstLoaded) && fetch();
},
{ deep: true }
);
watch( async function fetch() {
() => props.immediate, const { api } = props;
(v) => { if (!api || !isFunction(api)) return;
v && !isFirstLoaded.value && fetch(); loading.value = true;
} treeData.value = [];
); let result;
try {
onMounted(() => { result = await api(props.params);
props.immediate && fetch(); } catch (e) {
}); console.error(e);
}
async function fetch() { loading.value = false;
const { api } = props; if (!result) return;
if (!api || !isFunction(api)) return; if (!isArray(result)) {
loading.value = true; result = get(result, props.resultField);
treeData.value = []; }
let result; treeData.value = (result as Recordable[]) || [];
try { isFirstLoaded.value = true;
result = await api(props.params); emit('options-change', treeData.value);
} catch (e) { }
console.error(e); return { getAttrs, loading, handleChange };
} },
loading.value = false; });
if (!result) return;
if (!isArray(result)) {
result = get(result, props.resultField);
}
treeData.value = (result as Recordable[]) || [];
isFirstLoaded.value = true;
emit('options-change', treeData.value);
}
return { getAttrs, loading, handleChange };
},
});
</script> </script>

View File

@ -2,10 +2,10 @@
<a-col v-bind="actionColOpt" v-if="showActionButtonGroup"> <a-col v-bind="actionColOpt" v-if="showActionButtonGroup">
<div style="width: 100%" :style="{ textAlign: actionColOpt.style.textAlign }"> <div style="width: 100%" :style="{ textAlign: actionColOpt.style.textAlign }">
<FormItem> <FormItem>
<!-- update-begin-author:zyf Date:20211213 for调换按钮前后位置--> <!-- update-begin-author:zyf Date:20211213 for调换按钮前后位置-->
<slot name="submitBefore"></slot> <slot name="submitBefore"></slot>
<Button type="primary" class="mr-2" v-bind="getSubmitBtnOptions" @click="submitAction" v-if="showSubmitButton"> <Button type="primary" class="mr-2" v-bind="getSubmitBtnOptions" @click="submitAction" v-if="showSubmitButton">
<Icon icon="ant-design:search-outlined"></Icon> <Icon icon="ant-design:search-outlined"></Icon>
{{ getSubmitBtnOptions.text }} {{ getSubmitBtnOptions.text }}
</Button> </Button>
@ -14,7 +14,7 @@
<Icon icon="ic:baseline-restart-alt"></Icon> <Icon icon="ic:baseline-restart-alt"></Icon>
{{ getResetBtnOptions.text }} {{ getResetBtnOptions.text }}
</Button> </Button>
<!-- update-end-author:zyf Date:20211213 for调换按钮前后位置--> <!-- update-end-author:zyf Date:20211213 for调换按钮前后位置-->
<slot name="advanceBefore"></slot> <slot name="advanceBefore"></slot>
<Button type="link" size="small" @click="toggleAdvanced" v-if="showAdvancedButton && !hideAdvanceBtn"> <Button type="link" size="small" @click="toggleAdvanced" v-if="showAdvancedButton && !hideAdvanceBtn">
@ -75,9 +75,7 @@
const actionColOpt = computed(() => { const actionColOpt = computed(() => {
const { showAdvancedButton, actionSpan: span, actionColOptions } = props; const { showAdvancedButton, actionSpan: span, actionColOptions } = props;
const actionSpan = 24 - span; const actionSpan = 24 - span;
const advancedSpanObj = showAdvancedButton const advancedSpanObj = showAdvancedButton ? { span: actionSpan < 6 ? 24 : actionSpan } : {};
? { span: actionSpan < 6 ? 24 : actionSpan }
: {};
const actionColOpt: Partial<ColEx> = { const actionColOpt: Partial<ColEx> = {
style: { textAlign: 'right' }, style: { textAlign: 'right' },
span: showAdvancedButton ? 6 : 4, span: showAdvancedButton ? 6 : 4,

View File

@ -1,387 +1,352 @@
<script lang="tsx"> <script lang="tsx">
import type {PropType, Ref} from 'vue'; import type { PropType, Ref } from 'vue';
import type {FormActionType, FormProps} from '../types/form'; import type { FormActionType, FormProps } from '../types/form';
import type {FormSchema} from '../types/form'; import type { FormSchema } from '../types/form';
import type {ValidationRule} from 'ant-design-vue/lib/form/Form'; import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
import type {TableActionType} from '/@/components/Table'; import type { TableActionType } from '/@/components/Table';
import {defineComponent, computed, unref, toRefs} from 'vue'; import { defineComponent, computed, unref, toRefs } from 'vue';
import {Form, Col, Divider} from 'ant-design-vue'; import { Form, Col, Divider } from 'ant-design-vue';
import {componentMap} from '../componentMap'; import { componentMap } from '../componentMap';
import {BasicHelp} from '/@/components/Basic'; import { BasicHelp } from '/@/components/Basic';
import {isBoolean, isFunction, isNull} from '/@/utils/is'; import { isBoolean, isFunction, isNull } from '/@/utils/is';
import {getSlot} from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
import {createPlaceholderMessage, setComponentRuleType} from '../helper'; import { createPlaceholderMessage, setComponentRuleType } from '../helper';
import {upperFirst, cloneDeep} from 'lodash-es'; import { upperFirst, cloneDeep } from 'lodash-es';
import {useItemLabelWidth} from '../hooks/useLabelWidth'; import { useItemLabelWidth } from '../hooks/useLabelWidth';
import {useI18n} from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
name: 'BasicFormItem', name: 'BasicFormItem',
inheritAttrs: false, inheritAttrs: false,
props: { props: {
schema: { schema: {
type: Object as PropType<FormSchema>, type: Object as PropType<FormSchema>,
default: () => ({}), default: () => ({}),
}, },
formProps: { formProps: {
type: Object as PropType<FormProps>, type: Object as PropType<FormProps>,
default: () => ({}), default: () => ({}),
}, },
allDefaultValues: { allDefaultValues: {
type: Object as PropType<Recordable>, type: Object as PropType<Recordable>,
default: () => ({}), default: () => ({}),
}, },
formModel: { formModel: {
type: Object as PropType<Recordable>, type: Object as PropType<Recordable>,
default: () => ({}), default: () => ({}),
}, },
setFormModel: { setFormModel: {
type: Function as PropType<(key: string, value: any) => void>, type: Function as PropType<(key: string, value: any) => void>,
default: null, default: null,
}, },
tableAction: { tableAction: {
type: Object as PropType<TableActionType>, type: Object as PropType<TableActionType>,
}, },
formActionType: { formActionType: {
type: Object as PropType<FormActionType>, type: Object as PropType<FormActionType>,
}, },
}, },
setup(props, {slots}) { setup(props, { slots }) {
const {t} = useI18n(); const { t } = useI18n();
const {schema, formProps} = toRefs(props) as { const { schema, formProps } = toRefs(props) as {
schema: Ref<FormSchema>; schema: Ref<FormSchema>;
formProps: Ref<FormProps>; formProps: Ref<FormProps>;
}; };
const itemLabelWidthProp = useItemLabelWidth(schema, formProps); const itemLabelWidthProp = useItemLabelWidth(schema, formProps);
const getValues = computed(() => { const getValues = computed(() => {
const {allDefaultValues, formModel, schema} = props; const { allDefaultValues, formModel, schema } = props;
const {mergeDynamicData} = props.formProps; const { mergeDynamicData } = props.formProps;
return { return {
field: schema.field, field: schema.field,
model: formModel, model: formModel,
values: { values: {
...mergeDynamicData, ...mergeDynamicData,
...allDefaultValues, ...allDefaultValues,
...formModel, ...formModel,
} as Recordable, } as Recordable,
schema: schema, schema: schema,
}; };
}); });
const getComponentsProps = computed(() => { const getComponentsProps = computed(() => {
const {schema, tableAction, formModel, formActionType} = props; const { schema, tableAction, formModel, formActionType } = props;
let {componentProps = {}} = schema; let { componentProps = {} } = schema;
if (isFunction(componentProps)) { if (isFunction(componentProps)) {
componentProps = componentProps({schema, tableAction, formModel, formActionType}) ?? {}; componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
} }
if (schema.component === 'Divider') { if (schema.component === 'Divider') {
componentProps = Object.assign({type: 'horizontal'}, componentProps, { componentProps = Object.assign({ type: 'horizontal' }, componentProps, {
orientation: 'left', orientation: 'left',
plain: true, plain: true,
}); });
} }
return componentProps as Recordable; return componentProps as Recordable;
}); });
const getDisable = computed(() => { const getDisable = computed(() => {
const {disabled: globDisabled} = props.formProps; const { disabled: globDisabled } = props.formProps;
const {dynamicDisabled} = props.schema; const { dynamicDisabled } = props.schema;
const {disabled: itemDisabled = false} = unref(getComponentsProps); const { disabled: itemDisabled = false } = unref(getComponentsProps);
let disabled = !!globDisabled || itemDisabled; let disabled = !!globDisabled || itemDisabled;
if (isBoolean(dynamicDisabled)) { if (isBoolean(dynamicDisabled)) {
disabled = dynamicDisabled; disabled = dynamicDisabled;
} }
if (isFunction(dynamicDisabled)) { if (isFunction(dynamicDisabled)) {
disabled = dynamicDisabled(unref(getValues)); disabled = dynamicDisabled(unref(getValues));
} }
return disabled; return disabled;
}); });
function getShow(): { isShow: boolean; isIfShow: boolean } { function getShow(): { isShow: boolean; isIfShow: boolean } {
const {show, ifShow} = props.schema; const { show, ifShow } = props.schema;
const {showAdvancedButton} = props.formProps; const { showAdvancedButton } = props.formProps;
const itemIsAdvanced = showAdvancedButton const itemIsAdvanced = showAdvancedButton ? (isBoolean(props.schema.isAdvanced) ? props.schema.isAdvanced : true) : true;
? isBoolean(props.schema.isAdvanced)
? props.schema.isAdvanced
: true
: true;
let isShow = true; let isShow = true;
let isIfShow = true; let isIfShow = true;
if (isBoolean(show)) { if (isBoolean(show)) {
isShow = show; isShow = show;
} }
if (isBoolean(ifShow)) { if (isBoolean(ifShow)) {
isIfShow = ifShow; isIfShow = ifShow;
} }
if (isFunction(show)) { if (isFunction(show)) {
isShow = show(unref(getValues)); isShow = show(unref(getValues));
} }
if (isFunction(ifShow)) { if (isFunction(ifShow)) {
isIfShow = ifShow(unref(getValues)); isIfShow = ifShow(unref(getValues));
} }
isShow = isShow && itemIsAdvanced; isShow = isShow && itemIsAdvanced;
return {isShow, isIfShow}; return { isShow, isIfShow };
}
function handleRules(): ValidationRule[] {
const { rules: defRules = [], component, rulesMessageJoinLabel, label, dynamicRules, required } = props.schema;
if (isFunction(dynamicRules)) {
return dynamicRules(unref(getValues)) as ValidationRule[];
}
let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];
const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;
const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') ? rulesMessageJoinLabel : globalRulesMessageJoinLabel;
const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;
function validator(rule: any, value: any) {
const msg = rule.message || defaultMsg;
if (value === undefined || isNull(value)) {
//
return Promise.reject(msg);
} else if (Array.isArray(value) && value.length === 0) {
//
return Promise.reject(msg);
} else if (typeof value === 'string' && value.trim() === '') {
//
return Promise.reject(msg);
} else if (
typeof value === 'object' &&
Reflect.has(value, 'checked') &&
Reflect.has(value, 'halfChecked') &&
Array.isArray(value.checked) &&
Array.isArray(value.halfChecked) &&
value.checked.length === 0 &&
value.halfChecked.length === 0
) {
// tree
return Promise.reject(msg);
}
return Promise.resolve();
}
const getRequired = isFunction(required) ? required(unref(getValues)) : required;
if ((!rules || rules.length === 0) && getRequired) {
rules = [{ required: getRequired, validator }];
}
const requiredRuleIndex: number = rules.findIndex((rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator'));
if (requiredRuleIndex !== -1) {
const rule = rules[requiredRuleIndex];
const { isShow } = getShow();
if (!isShow) {
rule.required = false;
}
if (component) {
if (!Reflect.has(rule, 'type')) {
rule.type = component === 'InputNumber' ? 'number' : 'string';
} }
function handleRules(): ValidationRule[] { rule.message = rule.message || defaultMsg;
const {
rules: defRules = [],
component,
rulesMessageJoinLabel,
label,
dynamicRules,
required,
} = props.schema;
if (isFunction(dynamicRules)) { if (component.includes('Input') || component.includes('Textarea')) {
return dynamicRules(unref(getValues)) as ValidationRule[]; rule.whitespace = true;
}
let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];
const {rulesMessageJoinLabel: globalRulesMessageJoinLabel} = props.formProps;
const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel')
? rulesMessageJoinLabel
: globalRulesMessageJoinLabel;
const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;
function validator(rule: any, value: any) {
const msg = rule.message || defaultMsg;
if (value === undefined || isNull(value)) {
//
return Promise.reject(msg);
} else if (Array.isArray(value) && value.length === 0) {
//
return Promise.reject(msg);
} else if (typeof value === 'string' && value.trim() === '') {
//
return Promise.reject(msg);
} else if (
typeof value === 'object' &&
Reflect.has(value, 'checked') &&
Reflect.has(value, 'halfChecked') &&
Array.isArray(value.checked) &&
Array.isArray(value.halfChecked) &&
value.checked.length === 0 &&
value.halfChecked.length === 0
) {
// tree
return Promise.reject(msg);
}
return Promise.resolve();
}
const getRequired = isFunction(required) ? required(unref(getValues)) : required;
if ((!rules || rules.length === 0) && getRequired) {
rules = [{required: getRequired, validator}];
}
const requiredRuleIndex: number = rules.findIndex(
(rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator')
);
if (requiredRuleIndex !== -1) {
const rule = rules[requiredRuleIndex];
const {isShow} = getShow();
if (!isShow) {
rule.required = false;
}
if (component) {
if (!Reflect.has(rule, 'type')) {
rule.type = component === 'InputNumber' ? 'number' : 'string';
}
rule.message = rule.message || defaultMsg;
if (component.includes('Input') || component.includes('Textarea')) {
rule.whitespace = true;
}
const valueFormat = unref(getComponentsProps)?.valueFormat;
setComponentRuleType(rule, component, valueFormat);
}
}
// Maximum input length rule check
const characterInx = rules.findIndex((val) => val.max);
if (characterInx !== -1 && !rules[characterInx].validator) {
rules[characterInx].message =
rules[characterInx].message ||
t('component.form.maxTip', [rules[characterInx].max] as Recordable);
}
return rules;
} }
const valueFormat = unref(getComponentsProps)?.valueFormat;
setComponentRuleType(rule, component, valueFormat);
}
}
function renderComponent() { // Maximum input length rule check
const { const characterInx = rules.findIndex((val) => val.max);
renderComponentContent, if (characterInx !== -1 && !rules[characterInx].validator) {
component, rules[characterInx].message = rules[characterInx].message || t('component.form.maxTip', [rules[characterInx].max] as Recordable);
field, }
changeEvent = 'change', return rules;
valueField, }
} = props.schema;
const isCheck = component && ['Switch', 'Checkbox'].includes(component); function renderComponent() {
const { renderComponentContent, component, field, changeEvent = 'change', valueField } = props.schema;
const eventKey = `on${upperFirst(changeEvent)}`; const isCheck = component && ['Switch', 'Checkbox'].includes(component);
const on = { const eventKey = `on${upperFirst(changeEvent)}`;
[eventKey]: (...args: Nullable<Recordable>[]) => {
const [e] = args;
if (propsData[eventKey]) {
propsData[eventKey](...args);
}
const target = e ? e.target : null;
const value = target ? (isCheck ? target.checked : target.value) : e;
props.setFormModel(field, value);
},
};
const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
const {autoSetPlaceHolder, size} = props.formProps; const on = {
const propsData: Recordable = { [eventKey]: (...args: Nullable<Recordable>[]) => {
allowClear: true, const [e] = args;
getPopupContainer: (trigger: Element) => trigger.parentNode, if (propsData[eventKey]) {
size, propsData[eventKey](...args);
...unref(getComponentsProps), }
disabled: unref(getDisable), const target = e ? e.target : null;
}; const value = target ? (isCheck ? target.checked : target.value) : e;
props.setFormModel(field, value);
},
};
const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
const { autoSetPlaceHolder, size } = props.formProps;
const propsData: Recordable = {
allowClear: true,
getPopupContainer: (trigger: Element) => trigger.parentNode,
size,
...unref(getComponentsProps),
disabled: unref(getDisable),
};
const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder; const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;
// RangePicker place // RangePicker place
if (isCreatePlaceholder && component !== 'RangePicker' && component) { if (isCreatePlaceholder && component !== 'RangePicker' && component) {
//placeholder //placeholder
propsData.placeholder = propsData.placeholder = unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component) + props.schema.label;
unref(getComponentsProps)?.placeholder ||
createPlaceholderMessage(component) + props.schema.label;
} }
propsData.codeField = field; propsData.codeField = field;
propsData.formValues = unref(getValues); propsData.formValues = unref(getValues);
const bindValue: Recordable = { const bindValue: Recordable = {
[valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field], [valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
}; };
const compAttr: Recordable = { const compAttr: Recordable = {
...propsData, ...propsData,
...on, ...on,
...bindValue, ...bindValue,
}; };
if (!renderComponentContent) { if (!renderComponentContent) {
return <Comp {...compAttr} />; return <Comp {...compAttr} />;
} }
const compSlot = isFunction(renderComponentContent) const compSlot = isFunction(renderComponentContent)
? {...renderComponentContent(unref(getValues))} ? { ...renderComponentContent(unref(getValues)) }
: { : {
default: () => renderComponentContent, default: () => renderComponentContent,
}; };
return <Comp {...compAttr}>{compSlot}</Comp>; return <Comp {...compAttr}>{compSlot}</Comp>;
} }
/** /**
*渲染Label *渲染Label
* @updateBy:zyf * @updateBy:zyf
*/ */
function renderLabelHelpMessage() { function renderLabelHelpMessage() {
const {label, helpMessage, helpComponentProps, subLabel} = props.schema; const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
const renderLabel = subLabel ? ( const renderLabel = subLabel ? (
<span> <span>
{label} <span class="text-secondary">{subLabel}</span> {label} <span class="text-secondary">{subLabel}</span>
</span> </span>
) : ( ) : (
label label
); );
const getHelpMessage = isFunction(helpMessage) const getHelpMessage = isFunction(helpMessage) ? helpMessage(unref(getValues)) : helpMessage;
? helpMessage(unref(getValues)) if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
: helpMessage; return renderLabel;
if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) { }
return renderLabel; return (
} <span>
return (
<span>
{renderLabel} {renderLabel}
<BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} /> <BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} />
</span> </span>
); );
} }
function renderItem() { function renderItem() {
const {itemProps, slot, render, field, suffix, component} = props.schema; const { itemProps, slot, render, field, suffix, component } = props.schema;
const {labelCol, wrapperCol} = unref(itemLabelWidthProp); const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
const {colon} = props.formProps; const { colon } = props.formProps;
if (component === 'Divider') { if (component === 'Divider') {
return ( return (
<Col span={24}> <Col span={24}>
<Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider> <Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider>
</Col> </Col>
); );
} else { } else {
const getContent = () => { const getContent = () => {
return slot return slot ? getSlot(slots, slot, unref(getValues)) : render ? render(unref(getValues)) : renderComponent();
? getSlot(slots, slot, unref(getValues)) };
: render
? render(unref(getValues))
: renderComponent();
};
const showSuffix = !!suffix; const showSuffix = !!suffix;
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix; const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
return ( return (
<Form.Item <Form.Item
name={field} name={field}
colon={colon} colon={colon}
class={{'suffix-item': showSuffix}} class={{ 'suffix-item': showSuffix }}
{...(itemProps as Recordable)} {...(itemProps as Recordable)}
label={renderLabelHelpMessage()} label={renderLabelHelpMessage()}
rules={handleRules()} rules={handleRules()}
labelCol={labelCol} labelCol={labelCol}
wrapperCol={wrapperCol} wrapperCol={wrapperCol}
> >
<div style="display:flex"> <div style="display:flex">
{/* author: sunjianlei for: 【VUEN-744】此处加上 width: 100%; 因为要防止组件宽度超出 FormItem */ } {/* author: sunjianlei for: 【VUEN-744】此处加上 width: 100%; 因为要防止组件宽度超出 FormItem */}
<div style="flex:1; width: 100%;">{getContent()}</div> <div style="flex:1; width: 100%;">{getContent()}</div>
{showSuffix && <span class="suffix">{getSuffix}</span>} {showSuffix && <span class="suffix">{getSuffix}</span>}
</div> </div>
</Form.Item> </Form.Item>
); );
} }
} }
return () => { return () => {
const {colProps = {}, colSlot, renderColContent, component} = props.schema; const { colProps = {}, colSlot, renderColContent, component } = props.schema;
if (!componentMap.has(component)) { if (!componentMap.has(component)) {
return null; return null;
} }
const {baseColProps = {}} = props.formProps; const { baseColProps = {} } = props.formProps;
const realColProps = {...baseColProps, ...colProps}; const realColProps = { ...baseColProps, ...colProps };
const {isIfShow, isShow} = getShow(); const { isIfShow, isShow } = getShow();
const values = unref(getValues); const values = unref(getValues);
const getContent = () => { const getContent = () => {
return colSlot return colSlot ? getSlot(slots, colSlot, values) : renderColContent ? renderColContent(values) : renderItem();
? getSlot(slots, colSlot, values) };
: renderColContent
? renderColContent(values)
: renderItem();
};
return ( return (
isIfShow && ( isIfShow && (
<Col {...realColProps} v-show={isShow}> <Col {...realColProps} v-show={isShow}>
{getContent()} {getContent()}
</Col> </Col>
) )
); );
}; };
}, },
}); });
</script> </script>

View File

@ -16,13 +16,7 @@ export function createPlaceholderMessage(component: ComponentType) {
if (component.includes('Picker')) { if (component.includes('Picker')) {
return t('common.chooseText'); return t('common.chooseText');
} }
if ( if (component.includes('Select') || component.includes('Cascader') || component.includes('Checkbox') || component.includes('Radio') || component.includes('Switch')) {
component.includes('Select') ||
component.includes('Cascader') ||
component.includes('Checkbox') ||
component.includes('Radio') ||
component.includes('Switch')
) {
// return `请选择${label}`; // return `请选择${label}`;
return t('common.chooseText'); return t('common.chooseText');
} }
@ -35,11 +29,7 @@ function genType() {
return [...DATE_TYPE, 'RangePicker']; return [...DATE_TYPE, 'RangePicker'];
} }
export function setComponentRuleType( export function setComponentRuleType(rule: ValidationRule, component: ComponentType, valueFormat: string) {
rule: ValidationRule,
component: ComponentType,
valueFormat: string
) {
if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) { if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) {
rule.type = valueFormat ? 'string' : 'object'; rule.type = valueFormat ? 'string' : 'object';
} else if (['RangePicker', 'Upload', 'CheckboxGroup', 'TimePicker'].includes(component)) { } else if (['RangePicker', 'Upload', 'CheckboxGroup', 'TimePicker'].includes(component)) {

View File

@ -18,14 +18,7 @@ interface UseAdvancedContext {
defaultValueRef: Ref<Recordable>; defaultValueRef: Ref<Recordable>;
} }
export default function ({ export default function ({ advanceState, emit, getProps, getSchema, formModel, defaultValueRef }: UseAdvancedContext) {
advanceState,
emit,
getProps,
getSchema,
formModel,
defaultValueRef,
}: UseAdvancedContext) {
const { realWidthRef, screenEnum, screenRef } = useBreakpoint(); const { realWidthRef, screenEnum, screenRef } = useBreakpoint();
const getEmptySpan = computed((): number => { const getEmptySpan = computed((): number => {
@ -51,25 +44,20 @@ export default function ({
const debounceUpdateAdvanced = useDebounceFn(updateAdvanced, 30); const debounceUpdateAdvanced = useDebounceFn(updateAdvanced, 30);
watch( watch(
[() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)], [() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)],
() => { () => {
const { showAdvancedButton } = unref(getProps); const { showAdvancedButton } = unref(getProps);
if (showAdvancedButton) { if (showAdvancedButton) {
debounceUpdateAdvanced(); debounceUpdateAdvanced();
} }
}, },
{ immediate: true } { immediate: true }
); );
function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false, index = 0) { function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false, index = 0) {
const width = unref(realWidthRef); const width = unref(realWidthRef);
const mdWidth = const mdWidth = parseInt(itemCol.md as string) || parseInt(itemCol.xs as string) || parseInt(itemCol.sm as string) || (itemCol.span as number) || BASIC_COL_LEN;
parseInt(itemCol.md as string) ||
parseInt(itemCol.xs as string) ||
parseInt(itemCol.sm as string) ||
(itemCol.span as number) ||
BASIC_COL_LEN;
const lgWidth = parseInt(itemCol.lg as string) || mdWidth; const lgWidth = parseInt(itemCol.lg as string) || mdWidth;
const xlWidth = parseInt(itemCol.xl as string) || lgWidth; const xlWidth = parseInt(itemCol.xl as string) || lgWidth;
@ -84,7 +72,7 @@ export default function ({
itemColSum += xxlWidth; itemColSum += xxlWidth;
} }
let autoAdvancedCol = (unref(getProps).autoAdvancedCol ?? 3) let autoAdvancedCol = unref(getProps).autoAdvancedCol ?? 3;
if (isLastAction) { if (isLastAction) {
advanceState.hideAdvanceBtn = unref(getSchema).length <= autoAdvancedCol; advanceState.hideAdvanceBtn = unref(getSchema).length <= autoAdvancedCol;
@ -95,10 +83,7 @@ export default function ({
advanceState.isAdvanced = true; advanceState.isAdvanced = true;
} else */ } else */
// update-end--author:sunjianlei---date:20211108---for: 注释掉该逻辑使小于等于2行时也显示展开收起按钮 // update-end--author:sunjianlei---date:20211108---for: 注释掉该逻辑使小于等于2行时也显示展开收起按钮
if ( if (itemColSum > BASIC_COL_LEN * 2 && itemColSum <= BASIC_COL_LEN * (unref(getProps).autoAdvancedLine || 3)) {
itemColSum > BASIC_COL_LEN * 2 &&
itemColSum <= BASIC_COL_LEN * (unref(getProps).autoAdvancedLine || 3)
) {
advanceState.hideAdvanceBtn = false; advanceState.hideAdvanceBtn = false;
// 默认超过 3 行折叠 // 默认超过 3 行折叠
@ -107,8 +92,8 @@ export default function ({
advanceState.isAdvanced = !advanceState.isAdvanced; advanceState.isAdvanced = !advanceState.isAdvanced;
// update-begin--author:sunjianlei---date:20211108---for: 如果总列数大于 autoAdvancedCol就默认折叠 // update-begin--author:sunjianlei---date:20211108---for: 如果总列数大于 autoAdvancedCol就默认折叠
if (unref(getSchema).length > autoAdvancedCol) { if (unref(getSchema).length > autoAdvancedCol) {
advanceState.hideAdvanceBtn = false advanceState.hideAdvanceBtn = false;
advanceState.isAdvanced = false advanceState.isAdvanced = false;
} }
// update-end--author:sunjianlei---date:20211108---for: 如果总列数大于 autoAdvancedCol就默认折叠 // update-end--author:sunjianlei---date:20211108---for: 如果总列数大于 autoAdvancedCol就默认折叠
} }
@ -116,9 +101,9 @@ export default function ({
} }
if (itemColSum > BASIC_COL_LEN * (unref(getProps).alwaysShowLines || 1)) { if (itemColSum > BASIC_COL_LEN * (unref(getProps).alwaysShowLines || 1)) {
return { isAdvanced: advanceState.isAdvanced, itemColSum }; return { isAdvanced: advanceState.isAdvanced, itemColSum };
} else if (!advanceState.isAdvanced && (index + 1) > autoAdvancedCol) { } else if (!advanceState.isAdvanced && index + 1 > autoAdvancedCol) {
// 如果当前是收起状态,并且当前列下标 > autoAdvancedCol就隐藏 // 如果当前是收起状态,并且当前列下标 > autoAdvancedCol就隐藏
return { isAdvanced: false, itemColSum } return { isAdvanced: false, itemColSum };
} else { } else {
// The first line is always displayed // The first line is always displayed
return { isAdvanced: true, itemColSum }; return { isAdvanced: true, itemColSum };
@ -130,9 +115,9 @@ export default function ({
let realItemColSum = 0; let realItemColSum = 0;
const { baseColProps = {} } = unref(getProps); const { baseColProps = {} } = unref(getProps);
const schemas = unref(getSchema) const schemas = unref(getSchema);
for (let i = 0; i < schemas.length; i++) { for (let i = 0; i < schemas.length; i++) {
const schema = schemas[i] const schema = schemas[i];
const { show, colProps } = schema; const { show, colProps } = schema;
let isShow = true; let isShow = true;
@ -153,10 +138,7 @@ export default function ({
} }
if (isShow && (colProps || baseColProps)) { if (isShow && (colProps || baseColProps)) {
const { itemColSum: sum, isAdvanced } = getAdvanced( const { itemColSum: sum, isAdvanced } = getAdvanced({ ...baseColProps, ...colProps }, itemColSum, false, i);
{ ...baseColProps, ...colProps },
itemColSum, false, i,
);
itemColSum = sum || 0; itemColSum = sum || 0;
if (isAdvanced) { if (isAdvanced) {

View File

@ -9,12 +9,7 @@ interface UseAutoFocusContext {
isInitedDefault: Ref<boolean>; isInitedDefault: Ref<boolean>;
formElRef: Ref<FormActionType>; formElRef: Ref<FormActionType>;
} }
export async function useAutoFocus({ export async function useAutoFocus({ getSchema, getProps, formElRef, isInitedDefault }: UseAutoFocusContext) {
getSchema,
getProps,
formElRef,
isInitedDefault,
}: UseAutoFocusContext) {
watchEffect(async () => { watchEffect(async () => {
if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) { if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) {
return; return;

View File

@ -12,101 +12,95 @@ export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordab
type Props = Partial<DynamicProps<FormProps>>; type Props = Partial<DynamicProps<FormProps>>;
export function useForm(props?: Props): UseFormReturnType { export function useForm(props?: Props): UseFormReturnType {
const formRef = ref<Nullable<FormActionType>>(null); const formRef = ref<Nullable<FormActionType>>(null);
const loadedRef = ref<Nullable<boolean>>(false); const loadedRef = ref<Nullable<boolean>>(false);
async function getForm() { async function getForm() {
const form = unref(formRef); const form = unref(formRef);
if (!form) { if (!form) {
error( error('The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!');
'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!'
);
}
await nextTick();
return form as FormActionType;
} }
await nextTick();
return form as FormActionType;
}
function register(instance: FormActionType) { function register(instance: FormActionType) {
isProdMode() && isProdMode() &&
onUnmounted(() => { onUnmounted(() => {
formRef.value = null; formRef.value = null;
loadedRef.value = null; loadedRef.value = null;
}); });
if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return; if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return;
formRef.value = instance; formRef.value = instance;
loadedRef.value = true; loadedRef.value = true;
watch( watch(
() => props, () => props,
() => { () => {
props && instance.setProps(getDynamicProps(props)); props && instance.setProps(getDynamicProps(props));
}, },
{ {
immediate: true, immediate: true,
deep: true, deep: true,
} }
); );
} }
const methods: FormActionType = { const methods: FormActionType = {
scrollToField: async (name: NamePath, options?: ScrollOptions | undefined) => { scrollToField: async (name: NamePath, options?: ScrollOptions | undefined) => {
const form = await getForm(); const form = await getForm();
form.scrollToField(name, options); form.scrollToField(name, options);
}, },
setProps: async (formProps: Partial<FormProps>) => { setProps: async (formProps: Partial<FormProps>) => {
const form = await getForm(); const form = await getForm();
form.setProps(formProps); form.setProps(formProps);
}, },
updateSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => { updateSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => {
const form = await getForm(); const form = await getForm();
form.updateSchema(data); form.updateSchema(data);
}, },
resetSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => { resetSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => {
const form = await getForm(); const form = await getForm();
form.resetSchema(data); form.resetSchema(data);
}, },
clearValidate: async (name?: string | string[]) => { clearValidate: async (name?: string | string[]) => {
const form = await getForm(); const form = await getForm();
form.clearValidate(name); form.clearValidate(name);
}, },
resetFields: async () => { resetFields: async () => {
getForm().then(async (form) => { getForm().then(async (form) => {
await form.resetFields(); await form.resetFields();
}); });
}, },
removeSchemaByFiled: async (field: string | string[]) => { removeSchemaByFiled: async (field: string | string[]) => {
unref(formRef)?.removeSchemaByFiled(field); unref(formRef)?.removeSchemaByFiled(field);
}, },
// TODO promisify // TODO promisify
getFieldsValue: <T>() => { getFieldsValue: <T>() => {
return unref(formRef)?.getFieldsValue() as T; return unref(formRef)?.getFieldsValue() as T;
}, },
setFieldsValue: async <T>(values: T) => { setFieldsValue: async <T>(values: T) => {
const form = await getForm(); const form = await getForm();
form.setFieldsValue<T>(values); form.setFieldsValue<T>(values);
}, },
appendSchemaByField: async ( appendSchemaByField: async (schema: FormSchema, prefixField: string | undefined, first: boolean) => {
schema: FormSchema, const form = await getForm();
prefixField: string | undefined, form.appendSchemaByField(schema, prefixField, first);
first: boolean },
) => {
const form = await getForm();
form.appendSchemaByField(schema, prefixField, first);
},
submit: async (): Promise<any> => { submit: async (): Promise<any> => {
const form = await getForm(); const form = await getForm();
return form.submit(); return form.submit();
}, },
/** /**
* *
@ -125,9 +119,9 @@ export function useForm(props?: Props): UseFormReturnType {
} }
} }
} }
//--@updateBy-begin----author:liusq---date:20210916------for:处理区域事件字典信息------ //--@updateBy-begin----author:liusq---date:20210916------for:处理区域事件字典信息------
return handleRangeValue(props,values); return handleRangeValue(props, values);
//--@updateBy-end----author:liusq---date:20210916------for:处理区域事件字典信息------ //--@updateBy-end----author:liusq---date:20210916------for:处理区域事件字典信息------
}); });
return values; return values;
}, },

View File

@ -10,266 +10,249 @@ import { cloneDeep, uniqBy } from 'lodash-es';
import { error } from '/@/utils/log'; import { error } from '/@/utils/log';
interface UseFormActionContext { interface UseFormActionContext {
emit: EmitType; emit: EmitType;
getProps: ComputedRef<FormProps>; getProps: ComputedRef<FormProps>;
getSchema: ComputedRef<FormSchema[]>; getSchema: ComputedRef<FormSchema[]>;
formModel: Recordable; formModel: Recordable;
defaultValueRef: Ref<Recordable>; defaultValueRef: Ref<Recordable>;
formElRef: Ref<FormActionType>; formElRef: Ref<FormActionType>;
schemaRef: Ref<FormSchema[]>; schemaRef: Ref<FormSchema[]>;
handleFormValues: Fn; handleFormValues: Fn;
} }
export function useFormEvents({ export function useFormEvents({ emit, getProps, formModel, getSchema, defaultValueRef, formElRef, schemaRef, handleFormValues }: UseFormActionContext) {
emit, async function resetFields(): Promise<void> {
getProps, const { resetFunc, submitOnReset } = unref(getProps);
formModel, resetFunc && isFunction(resetFunc) && (await resetFunc());
getSchema,
defaultValueRef,
formElRef,
schemaRef,
handleFormValues,
}: UseFormActionContext) {
async function resetFields(): Promise<void> {
const { resetFunc, submitOnReset } = unref(getProps);
resetFunc && isFunction(resetFunc) && (await resetFunc());
const formEl = unref(formElRef); const formEl = unref(formElRef);
if (!formEl) return; if (!formEl) return;
Object.keys(formModel).forEach((key) => { Object.keys(formModel).forEach((key) => {
formModel[key] = defaultValueRef.value[key]; formModel[key] = defaultValueRef.value[key];
}); });
clearValidate(); clearValidate();
emit('reset', toRaw(formModel)); emit('reset', toRaw(formModel));
submitOnReset && handleSubmit(); submitOnReset && handleSubmit();
} }
/** /**
* @description: Set form value * @description: Set form value
*/ */
async function setFieldsValue(values: Recordable): Promise<void> { async function setFieldsValue(values: Recordable): Promise<void> {
const fields = unref(getSchema) const fields = unref(getSchema)
.map((item) => item.field) .map((item) => item.field)
.filter(Boolean); .filter(Boolean);
const validKeys: string[] = []; const validKeys: string[] = [];
Object.keys(values).forEach((key) => { Object.keys(values).forEach((key) => {
const schema = unref(getSchema).find((item) => item.field === key); const schema = unref(getSchema).find((item) => item.field === key);
let value = values[key]; let value = values[key];
const hasKey = Reflect.has(values, key); const hasKey = Reflect.has(values, key);
value = handleInputNumberValue(schema?.component, value); value = handleInputNumberValue(schema?.component, value);
// 0| '' is allow // 0| '' is allow
if (hasKey && fields.includes(key)) { if (hasKey && fields.includes(key)) {
// time type // time type
if (itemIsDateType(key)) { if (itemIsDateType(key)) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
const arr: any[] = []; const arr: any[] = [];
for (const ele of value) { for (const ele of value) {
arr.push(ele ? dateUtil(ele) : null); arr.push(ele ? dateUtil(ele) : null);
}
formModel[key] = arr;
} else {
const { componentProps } = schema || {};
let _props = componentProps as any;
if (typeof componentProps === 'function') {
_props = _props({ formModel });
}
formModel[key] = value ? (_props?.valueFormat ? value : dateUtil(value)) : null;
}
} else {
formModel[key] = value;
}
validKeys.push(key);
} }
}); formModel[key] = arr;
validateFields(validKeys).catch((_) => {}); } else {
} const { componentProps } = schema || {};
/** let _props = componentProps as any;
* @description: Delete based on field name if (typeof componentProps === 'function') {
*/ _props = _props({ formModel });
async function removeSchemaByFiled(fields: string | string[]): Promise<void> {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
if (!fields) {
return;
}
let fieldList: string[] = isString(fields) ? [fields] : fields;
if (isString(fields)) {
fieldList = [fields];
}
for (const field of fieldList) {
_removeSchemaByFiled(field, schemaList);
}
schemaRef.value = schemaList;
}
/**
* @description: Delete based on field name
*/
function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void {
if (isString(field)) {
const index = schemaList.findIndex((schema) => schema.field === field);
if (index !== -1) {
delete formModel[field];
schemaList.splice(index, 1);
} }
formModel[key] = value ? (_props?.valueFormat ? value : dateUtil(value)) : null;
}
} else {
formModel[key] = value;
} }
validKeys.push(key);
}
});
validateFields(validKeys).catch((_) => {});
}
/**
* @description: Delete based on field name
*/
async function removeSchemaByFiled(fields: string | string[]): Promise<void> {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
if (!fields) {
return;
} }
/** let fieldList: string[] = isString(fields) ? [fields] : fields;
* @description: Insert after a certain field, if not insert the last if (isString(fields)) {
*/ fieldList = [fields];
async function appendSchemaByField(schema: FormSchema, prefixField?: string, first = false) { }
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); for (const field of fieldList) {
_removeSchemaByFiled(field, schemaList);
}
schemaRef.value = schemaList;
}
const index = schemaList.findIndex((schema) => schema.field === prefixField); /**
const hasInList = schemaList.some((item) => item.field === prefixField || schema.field); * @description: Delete based on field name
*/
function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void {
if (isString(field)) {
const index = schemaList.findIndex((schema) => schema.field === field);
if (index !== -1) {
delete formModel[field];
schemaList.splice(index, 1);
}
}
}
if (!hasInList) return; /**
* @description: Insert after a certain field, if not insert the last
*/
async function appendSchemaByField(schema: FormSchema, prefixField?: string, first = false) {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
if (!prefixField || index === -1 || first) { const index = schemaList.findIndex((schema) => schema.field === prefixField);
first ? schemaList.unshift(schema) : schemaList.push(schema); const hasInList = schemaList.some((item) => item.field === prefixField || schema.field);
schemaRef.value = schemaList;
return; if (!hasInList) return;
}
if (index !== -1) { if (!prefixField || index === -1 || first) {
schemaList.splice(index + 1, 0, schema); first ? schemaList.unshift(schema) : schemaList.push(schema);
} schemaRef.value = schemaList;
schemaRef.value = schemaList; return;
}
if (index !== -1) {
schemaList.splice(index + 1, 0, schema);
}
schemaRef.value = schemaList;
}
async function resetSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
let updateData: Partial<FormSchema>[] = [];
if (isObject(data)) {
updateData.push(data as FormSchema);
}
if (isArray(data)) {
updateData = [...data];
} }
async function resetSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { const hasField = updateData.every((item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field));
let updateData: Partial<FormSchema>[] = [];
if (isObject(data)) { if (!hasField) {
updateData.push(data as FormSchema); error('All children of the form Schema array that need to be updated must contain the `field` field');
return;
}
schemaRef.value = updateData as FormSchema[];
}
async function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
let updateData: Partial<FormSchema>[] = [];
if (isObject(data)) {
updateData.push(data as FormSchema);
}
if (isArray(data)) {
updateData = [...data];
}
const hasField = updateData.every((item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field));
if (!hasField) {
error('All children of the form Schema array that need to be updated must contain the `field` field');
return;
}
const schema: FormSchema[] = [];
updateData.forEach((item) => {
unref(getSchema).forEach((val) => {
if (val.field === item.field) {
const newSchema = deepMerge(val, item);
schema.push(newSchema as FormSchema);
} else {
schema.push(val);
} }
if (isArray(data)) { });
updateData = [...data]; });
schemaRef.value = uniqBy(schema, 'field');
}
function getFieldsValue(): Recordable {
const formEl = unref(formElRef);
if (!formEl) return {};
return handleFormValues(toRaw(unref(formModel)));
}
/**
* @description: Is it time
*/
function itemIsDateType(key: string) {
return unref(getSchema).some((item) => {
return item.field === key ? dateItemType.includes(item.component) : false;
});
}
async function validateFields(nameList?: NamePath[] | undefined) {
return unref(formElRef)?.validateFields(nameList);
}
async function validate(nameList?: NamePath[] | undefined) {
return await unref(formElRef)?.validate(nameList);
}
async function clearValidate(name?: string | string[]) {
await unref(formElRef)?.clearValidate(name);
}
async function scrollToField(name: NamePath, options?: ScrollOptions | undefined) {
await unref(formElRef)?.scrollToField(name, options);
}
/**
* @description: Form submission
*/
async function handleSubmit(e?: Event): Promise<void> {
e && e.preventDefault();
const { submitFunc } = unref(getProps);
if (submitFunc && isFunction(submitFunc)) {
await submitFunc();
return;
}
const formEl = unref(formElRef);
if (!formEl) return;
try {
const values = await validate();
//update-begin---author:zhangdaihao Date:20140212 for[bug号]树机构调整------------
//--updateBy-begin----author:zyf---date:20211206------for:对查询表单提交的数组处理成字符串------
for (let key in values) {
if (values[key] instanceof Array) {
let valueType = getValueType(getProps, key);
if (valueType === 'string') {
values[key] = values[key].join(',');
}
} }
}
const hasField = updateData.every( //--updateBy-end----author:zyf---date:20211206------for:对查询表单提交的数组处理成字符串------
(item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field) const res = handleFormValues(values);
); emit('submit', res);
} catch (error) {
if (!hasField) { throw new Error(error);
error(
'All children of the form Schema array that need to be updated must contain the `field` field'
);
return;
}
schemaRef.value = updateData as FormSchema[];
} }
}
async function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { return {
let updateData: Partial<FormSchema>[] = []; handleSubmit,
if (isObject(data)) { clearValidate,
updateData.push(data as FormSchema); validate,
} validateFields,
if (isArray(data)) { getFieldsValue,
updateData = [...data]; updateSchema,
} resetSchema,
appendSchemaByField,
const hasField = updateData.every( removeSchemaByFiled,
(item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field) resetFields,
); setFieldsValue,
scrollToField,
if (!hasField) { };
error(
'All children of the form Schema array that need to be updated must contain the `field` field'
);
return;
}
const schema: FormSchema[] = [];
updateData.forEach((item) => {
unref(getSchema).forEach((val) => {
if (val.field === item.field) {
const newSchema = deepMerge(val, item);
schema.push(newSchema as FormSchema);
} else {
schema.push(val);
}
});
});
schemaRef.value = uniqBy(schema, 'field');
}
function getFieldsValue(): Recordable {
const formEl = unref(formElRef);
if (!formEl) return {};
return handleFormValues(toRaw(unref(formModel)));
}
/**
* @description: Is it time
*/
function itemIsDateType(key: string) {
return unref(getSchema).some((item) => {
return item.field === key ? dateItemType.includes(item.component) : false;
});
}
async function validateFields(nameList?: NamePath[] | undefined) {
return unref(formElRef)?.validateFields(nameList);
}
async function validate(nameList?: NamePath[] | undefined) {
return await unref(formElRef)?.validate(nameList);
}
async function clearValidate(name?: string | string[]) {
await unref(formElRef)?.clearValidate(name);
}
async function scrollToField(name: NamePath, options?: ScrollOptions | undefined) {
await unref(formElRef)?.scrollToField(name, options);
}
/**
* @description: Form submission
*/
async function handleSubmit(e?: Event): Promise<void> {
e && e.preventDefault();
const { submitFunc } = unref(getProps);
if (submitFunc && isFunction(submitFunc)) {
await submitFunc();
return;
}
const formEl = unref(formElRef);
if (!formEl) return;
try {
const values = await validate();
//update-begin---author:zhangdaihao Date:20140212 for[bug号]树机构调整------------
//--updateBy-begin----author:zyf---date:20211206------for:对查询表单提交的数组处理成字符串------
for (let key in values) {
if (values[key] instanceof Array) {
let valueType = getValueType(getProps, key);
if (valueType === 'string') {
values[key] = values[key].join(',');
}
}
}
//--updateBy-end----author:zyf---date:20211206------for:对查询表单提交的数组处理成字符串------
const res = handleFormValues(values);
emit('submit', res);
} catch (error) {
throw new Error(error);
}
}
return {
handleSubmit,
clearValidate,
validate,
validateFields,
getFieldsValue,
updateSchema,
resetSchema,
appendSchemaByField,
removeSchemaByFiled,
resetFields,
setFieldsValue,
scrollToField,
};
} }

View File

@ -12,12 +12,7 @@ interface UseFormValuesContext {
getProps: ComputedRef<FormProps>; getProps: ComputedRef<FormProps>;
formModel: Recordable; formModel: Recordable;
} }
export function useFormValues({ export function useFormValues({ defaultValueRef, getSchema, formModel, getProps }: UseFormValuesContext) {
defaultValueRef,
getSchema,
formModel,
getProps,
}: UseFormValuesContext) {
// Processing form values // Processing form values
function handleFormValues(values: Recordable) { function handleFormValues(values: Recordable) {
if (!isObject(values)) { if (!isObject(values)) {
@ -43,10 +38,9 @@ export function useFormValues({
} }
set(res, key, value); set(res, key, value);
} }
return handleRangeValue(getProps,res); return handleRangeValue(getProps, res);
} }
function initDefault() { function initDefault() {
const schemas = unref(getSchema); const schemas = unref(getSchema);
const obj: Recordable = {}; const obj: Recordable = {};

View File

@ -10,20 +10,16 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
const { labelCol = {}, wrapperCol = {} } = schemaItem.itemProps || {}; const { labelCol = {}, wrapperCol = {} } = schemaItem.itemProps || {};
const { labelWidth, disabledLabelWidth } = schemaItem; const { labelWidth, disabledLabelWidth } = schemaItem;
const { const { labelWidth: globalLabelWidth, labelCol: globalLabelCol, wrapperCol: globWrapperCol } = unref(propsRef);
labelWidth: globalLabelWidth,
labelCol: globalLabelCol,
wrapperCol: globWrapperCol,
} = unref(propsRef);
// update-begin--author:sunjianlei---date:20211104---for: 禁用全局 labelWidth不自动设置 textAlign -------- // update-begin--author:sunjianlei---date:20211104---for: 禁用全局 labelWidth不自动设置 textAlign --------
if (disabledLabelWidth) { if (disabledLabelWidth) {
return { labelCol, wrapperCol } return { labelCol, wrapperCol };
} }
// update-begin--author:sunjianlei---date:20211104---for: 禁用全局 labelWidth不自动设置 textAlign -------- // update-begin--author:sunjianlei---date:20211104---for: 禁用全局 labelWidth不自动设置 textAlign --------
// If labelWidth is set globally, all items setting // If labelWidth is set globally, all items setting
if ((!globalLabelWidth && !labelWidth && !globalLabelCol)) { if (!globalLabelWidth && !labelWidth && !globalLabelCol) {
labelCol.style = { labelCol.style = {
textAlign: 'left', textAlign: 'left',
}; };

View File

@ -1,119 +1,114 @@
<template> <template>
<div v-for="(param, index) in dynamicInput.params" :key="index" style="display: flex"> <div v-for="(param, index) in dynamicInput.params" :key="index" style="display: flex">
<a-input placeholder="请输入参数key" v-model:value="param.label" style="width: 30%;margin-bottom: 5px" @input="emitChange"/> <a-input placeholder="请输入参数key" v-model:value="param.label" style="width: 30%; margin-bottom: 5px" @input="emitChange" />
<a-input placeholder="请输入参数value" v-model:value="param.value" style="width: 30%;margin: 0 0 5px 5px" @input="emitChange"/> <a-input placeholder="请输入参数value" v-model:value="param.value" style="width: 30%; margin: 0 0 5px 5px" @input="emitChange" />
<MinusCircleOutlined <MinusCircleOutlined v-if="dynamicInput.params.length > 1" class="dynamic-delete-button" @click="remove(param)" style="width: 50px"></MinusCircleOutlined>
v-if="dynamicInput.params.length > 1" </div>
class="dynamic-delete-button" <div>
@click="remove(param)" <a-button type="dashed" style="width: 60%" @click="add">
style="width: 50px" <PlusOutlined />
></MinusCircleOutlined> 新增
</div> </a-button>
<div> </div>
<a-button type="dashed" style="width: 60%" @click="add">
<PlusOutlined/>
新增
</a-button>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {MinusCircleOutlined, PlusOutlined} from '@ant-design/icons-vue'; import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons-vue';
import {defineComponent, reactive, ref, UnwrapRef, watchEffect} from 'vue'; import { defineComponent, reactive, ref, UnwrapRef, watchEffect } from 'vue';
import {propTypes} from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { isEmpty } from '/@/utils/is' import { isEmpty } from '/@/utils/is';
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core'; import { tryOnMounted, tryOnUnmounted } from '@vueuse/core';
interface Params { interface Params {
label: string; label: string;
value: string; value: string;
} }
export default defineComponent({ export default defineComponent({
name: 'JAddInput', name: 'JAddInput',
props: { props: {
value: propTypes.string.def('') value: propTypes.string.def(''),
}, },
emits: ['change', 'update:value'], emits: ['change', 'update:value'],
setup(props, {emit}) { setup(props, { emit }) {
//input //input
const dynamicInput: UnwrapRef<{ params: Params[] }> = reactive({params: []}); const dynamicInput: UnwrapRef<{ params: Params[] }> = reactive({ params: [] });
//Input //Input
const remove = (item: Params) => { const remove = (item: Params) => {
let index = dynamicInput.params.indexOf(item); let index = dynamicInput.params.indexOf(item);
if (index !== -1) { if (index !== -1) {
dynamicInput.params.splice(index, 1); dynamicInput.params.splice(index, 1);
} }
emitChange() emitChange();
}; };
//Input //Input
const add = () => { const add = () => {
dynamicInput.params.push({ dynamicInput.params.push({
label: '', label: '',
value: '', value: '',
}); });
emitChange() emitChange();
}; };
//value //value
watchEffect(() => { watchEffect(() => {
initVal(); initVal();
}); });
/** /**
* 初始化数值 * 初始化数值
*/ */
function initVal() { function initVal() {
console.log("props.value",props.value) console.log('props.value', props.value);
dynamicInput.params = []; dynamicInput.params = [];
if(props.value && props.value.indexOf("{")==0){ if (props.value && props.value.indexOf('{') == 0) {
let jsonObj = JSON.parse(props.value); let jsonObj = JSON.parse(props.value);
Object.keys(jsonObj).forEach((key) => { Object.keys(jsonObj).forEach((key) => {
dynamicInput.params.push({label: key, value: jsonObj[key]}); dynamicInput.params.push({ label: key, value: jsonObj[key] });
}); });
} }
} }
/** /**
* 数值改变 * 数值改变
*/ */
function emitChange() { function emitChange() {
let obj = {}; let obj = {};
if (dynamicInput.params.length > 0) { if (dynamicInput.params.length > 0) {
dynamicInput.params.forEach(item => { dynamicInput.params.forEach((item) => {
obj[item['label']] = item['value'] obj[item['label']] = item['value'];
}) });
} }
emit("change", isEmpty(obj)?'': JSON.stringify(obj)); emit('change', isEmpty(obj) ? '' : JSON.stringify(obj));
emit("update:value",isEmpty(obj)?'': JSON.stringify(obj)) emit('update:value', isEmpty(obj) ? '' : JSON.stringify(obj));
} }
return { return {
dynamicInput, dynamicInput,
emitChange, emitChange,
remove, remove,
add, add,
}; };
}, },
components: { components: {
MinusCircleOutlined, MinusCircleOutlined,
PlusOutlined, PlusOutlined,
}, },
}); });
</script> </script>
<style scoped> <style scoped>
.dynamic-delete-button { .dynamic-delete-button {
cursor: pointer; cursor: pointer;
position: relative; position: relative;
top: 4px; top: 4px;
font-size: 24px; font-size: 24px;
color: #999; color: #999;
transition: all 0.3s; transition: all 0.3s;
} }
.dynamic-delete-button:hover { .dynamic-delete-button:hover {
color: #777; color: #777;
} }
.dynamic-delete-button[disabled] { .dynamic-delete-button[disabled] {
cursor: not-allowed; cursor: not-allowed;
opacity: 0.5; opacity: 0.5;
} }
</style> </style>

View File

@ -1,59 +1,56 @@
<template> <template>
<Cascader v-bind="attrs" v-model:value="state" :options="getOptions" @change="handleChange"/> <Cascader v-bind="attrs" v-model:value="state" :options="getOptions" @change="handleChange" />
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, PropType, ref,reactive, watchEffect, computed, unref, watch, onMounted} from 'vue'; import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted } from 'vue';
import {Cascader} from 'ant-design-vue'; import { Cascader } from 'ant-design-vue';
import {provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus} from "../../utils/areaDataUtil"; import { provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus } from '../../utils/areaDataUtil';
import {useRuleFormItem} from "/@/hooks/component/useFormItem"; import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import {propTypes} from "/@/utils/propTypes"; import { propTypes } from '/@/utils/propTypes';
import {useAttrs} from "/@/hooks/core/useAttrs"; import { useAttrs } from '/@/hooks/core/useAttrs';
export default defineComponent({ export default defineComponent({
name: 'JAreaLinkage', name: 'JAreaLinkage',
components: { components: {
Cascader Cascader,
}, },
inheritAttrs: false, inheritAttrs: false,
props: { props: {
value: propTypes.oneOfType([ value: propTypes.oneOfType([propTypes.object, propTypes.array]),
propTypes.object, //
propTypes.array showArea: propTypes.bool.def(true),
]), //
// showAll: propTypes.bool.def(false),
showArea:propTypes.bool.def(true), },
// emits: ['options-change', 'change'],
showAll:propTypes.bool.def(false) setup(props, { emit, refs }) {
}, const emitData = ref<any[]>([]);
emits: ['options-change', 'change'], const attrs = useAttrs();
setup(props, {emit,refs}) { const [state] = useRuleFormItem(props, 'value', 'change', emitData);
const emitData = ref<any[]>([]); const getOptions = computed(() => {
const attrs = useAttrs(); if (props.showArea && props.showAll) {
const [state] = useRuleFormItem(props, 'value', 'change', emitData); return regionDataPlus;
const getOptions = computed(() => { }
if(props.showArea&&props.showAll){ if (props.showArea && !props.showAll) {
return regionDataPlus return regionData;
} }
if(props.showArea&&!props.showAll){ if (!props.showArea && !props.showAll) {
return regionData return provinceAndCityData;
} }
if(!props.showArea&&!props.showAll){ if (!props.showArea && props.showAll) {
return provinceAndCityData return provinceAndCityDataPlus;
} }
if(!props.showArea&&props.showAll){ });
return provinceAndCityDataPlus function handleChange(_, ...args) {
} emitData.value = args;
}); console.info(emitData);
function handleChange(_, ...args) { }
emitData.value = args; return {
console.info(emitData) state,
} attrs,
return { regionData,
state, getOptions,
attrs, handleChange,
regionData, };
getOptions, },
handleChange });
};
},
});
</script> </script>

View File

@ -1,153 +1,153 @@
<template> <template>
<div class="area-select"> <div class="area-select">
<!--省份--> <!--省份-->
<a-select v-model:value="province" @change="proChange" allowClear> <a-select v-model:value="province" @change="proChange" allowClear>
<template v-for="item in provinceOptions" :key="`${item.value}`"> <template v-for="item in provinceOptions" :key="`${item.value}`">
<a-select-option :value="item.value">{{ item.label }}</a-select-option> <a-select-option :value="item.value">{{ item.label }}</a-select-option>
</template> </template>
</a-select> </a-select>
<!--城市--> <!--城市-->
<a-select v-if="level>=2" v-model:value="city" @change="cityChange"> <a-select v-if="level >= 2" v-model:value="city" @change="cityChange">
<template v-for="item in cityOptions" :key="`${item.value}`"> <template v-for="item in cityOptions" :key="`${item.value}`">
<a-select-option :value="item.value">{{ item.label }}</a-select-option> <a-select-option :value="item.value">{{ item.label }}</a-select-option>
</template> </template>
</a-select> </a-select>
<!--地区--> <!--地区-->
<a-select v-if="level>=3" v-model:value="area" @change="areaChange"> <a-select v-if="level >= 3" v-model:value="area" @change="areaChange">
<template v-for="item in areaOptions" :key="`${item.value}`"> <template v-for="item in areaOptions" :key="`${item.value}`">
<a-select-option :value="item.value">{{ item.label }}</a-select-option> <a-select-option :value="item.value">{{ item.label }}</a-select-option>
</template> </template>
</a-select> </a-select>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted, onUnmounted, toRefs} from 'vue'; import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted, onUnmounted, toRefs } from 'vue';
import {propTypes} from "/@/utils/propTypes"; import { propTypes } from '/@/utils/propTypes';
import {useRuleFormItem} from '/@/hooks/component/useFormItem'; import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import {provinceOptions, getDataByCode, getRealCode} from "../../utils/areaDataUtil"; import { provinceOptions, getDataByCode, getRealCode } from '../../utils/areaDataUtil';
export default defineComponent({ export default defineComponent({
name: 'JAreaSelect', name: 'JAreaSelect',
props: { props: {
value: [Array, String], value: [Array, String],
province: [String], province: [String],
city: [String], city: [String],
area: [String], area: [String],
level: propTypes.number.def(3), level: propTypes.number.def(3),
}, },
emits: ['change','update:value'], emits: ['change', 'update:value'],
setup(props, {emit, refs}) { setup(props, { emit, refs }) {
const emitData = ref<any[]>([]); const emitData = ref<any[]>([]);
// //
const pca = reactive({ const pca = reactive({
province: '', province: '',
city: '', city: '',
area: '', area: '',
}); });
// //
const [state] = useRuleFormItem(props, 'value', 'change', emitData); const [state] = useRuleFormItem(props, 'value', 'change', emitData);
// //
const cityOptions = computed(() => { const cityOptions = computed(() => {
return pca.province ? getDataByCode(pca.province) : []; return pca.province ? getDataByCode(pca.province) : [];
}); });
// //
const areaOptions = computed(() => { const areaOptions = computed(() => {
return pca.city ? getDataByCode(pca.city) : []; return pca.city ? getDataByCode(pca.city) : [];
}); });
/** /**
* 监听props值 * 监听props值
*/ */
watchEffect(() => { watchEffect(() => {
props && initValue(); props && initValue();
}); });
/** /**
* 监听组件值变化 * 监听组件值变化
*/ */
watch(pca, (newVal) => { watch(pca, (newVal) => {
if(!props.value){ if (!props.value) {
emit("update:province",pca.province); emit('update:province', pca.province);
emit("update:city",pca.city); emit('update:city', pca.city);
emit("update:area",pca.area); emit('update:area', pca.area);
} }
}); });
/** /**
* 数据初始化 * 数据初始化
*/ */
function initValue() { function initValue() {
if (props.value) { if (props.value) {
// //
if (Array.isArray(props.value)) { if (Array.isArray(props.value)) {
pca.province = props.value[0]; pca.province = props.value[0];
pca.city = props.value[1] ? props.value[1] : ''; pca.city = props.value[1] ? props.value[1] : '';
pca.area = props.value[2] ? props.value[2] : ''; pca.area = props.value[2] ? props.value[2] : '';
} else { } else {
// //
let valueArr = getRealCode(props.value, props.level); let valueArr = getRealCode(props.value, props.level);
if (valueArr) { if (valueArr) {
pca.province = valueArr[0]; pca.province = valueArr[0];
pca.city = props.level >= 2 && valueArr[1] ? valueArr[1] : ''; pca.city = props.level >= 2 && valueArr[1] ? valueArr[1] : '';
pca.area = props.level >= 3 && valueArr[2] ? valueArr[2] : ''; pca.area = props.level >= 3 && valueArr[2] ? valueArr[2] : '';
}
}
}else{
//
pca.province = props.province?props.province:'';
pca.city = props.city?props.city:'';
pca.area = props.area?props.area:'';
}
} }
}
} else {
//
pca.province = props.province ? props.province : '';
pca.city = props.city ? props.city : '';
pca.area = props.area ? props.area : '';
}
}
/** /**
* 省份change事件 * 省份change事件
*/ */
function proChange(val) { function proChange(val) {
pca.city = (val && getDataByCode(val)[0]?.value); pca.city = val && getDataByCode(val)[0]?.value;
pca.area = (pca.city && getDataByCode(pca.city)[0]?.value); pca.area = pca.city && getDataByCode(pca.city)[0]?.value;
state.value = props.level <= 1 ? val : (props.level <= 2 ? pca.city : pca.area); state.value = props.level <= 1 ? val : props.level <= 2 ? pca.city : pca.area;
emit("update:value",unref(state)); emit('update:value', unref(state));
} }
/** /**
* 城市change事件 * 城市change事件
*/ */
function cityChange(val) { function cityChange(val) {
pca.area = (val && getDataByCode(val)[0]?.value); pca.area = val && getDataByCode(val)[0]?.value;
state.value = props.level <= 2 ? val : pca.area; state.value = props.level <= 2 ? val : pca.area;
emit("update:value",unref(state)); emit('update:value', unref(state));
} }
/** /**
* 区域change事件 * 区域change事件
*/ */
function areaChange(val) { function areaChange(val) {
state.value = val; state.value = val;
emit("update:value",unref(state)); emit('update:value', unref(state));
} }
return { return {
...toRefs(pca), ...toRefs(pca),
provinceOptions, provinceOptions,
cityOptions, cityOptions,
areaOptions, areaOptions,
proChange, proChange,
cityChange, cityChange,
areaChange areaChange,
}; };
}, },
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.area-select { .area-select {
width: 100%; width: 100%;
display: flex; display: flex;
.ant-select{ .ant-select {
width: 33.3%; width: 33.3%;
}
.ant-select:not(:first-child) {
margin-left: 10px;
}
} }
.ant-select:not(:first-child) {
margin-left: 10px;
}
}
</style> </style>

View File

@ -1,18 +1,19 @@
<!--下拉树--> <!--下拉树-->
<template> <template>
<a-tree-select <a-tree-select
allowClear allowClear
labelInValue labelInValue
style="width: 100%" style="width: 100%"
:disabled="disabled" :disabled="disabled"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }" :dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
:placeholder="placeholder" :placeholder="placeholder"
:loadData="asyncLoadTreeData" :loadData="asyncLoadTreeData"
:value="treeValue" :value="treeValue"
:treeData="treeData" :treeData="treeData"
:multiple="multiple" :multiple="multiple"
@change="onChange"> @change="onChange"
</a-tree-select> >
</a-tree-select>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, unref, watch } from 'vue'; import { defineComponent, ref, unref, watch } from 'vue';
@ -28,10 +29,7 @@
components: {}, components: {},
inheritAttrs: false, inheritAttrs: false,
props: { props: {
value: propTypes.oneOfType([ value: propTypes.oneOfType([propTypes.string, propTypes.array]),
propTypes.string,
propTypes.array,
]),
placeholder: { placeholder: {
type: String, type: String,
default: '请选择', default: '请选择',
@ -49,7 +47,7 @@
}, },
// //
multiple: { multiple: {
type: [Boolean,String], type: [Boolean, String],
default: false, default: false,
}, },
loadTriggleChange: { loadTriggleChange: {
@ -86,14 +84,14 @@
() => { () => {
loadItemByCode(); loadItemByCode();
}, },
{ deep: true }, { deep: true }
); );
watch( watch(
() => props.pcode, () => props.pcode,
() => { () => {
loadRoot(); loadRoot();
}, },
{ deep: true, immediate: true }, { deep: true, immediate: true }
); );
function loadRoot() { function loadRoot() {
@ -103,7 +101,7 @@
condition: props.condition, condition: props.condition,
}; };
console.info(param); console.info(param);
loadTreeData(param).then(res => { loadTreeData(param).then((res) => {
for (let i of res) { for (let i of res) {
i.value = i.key; i.value = i.key;
if (i.leaf == false) { if (i.leaf == false) {
@ -114,15 +112,13 @@
} }
treeData.value = res; treeData.value = res;
}); });
} }
function loadItemByCode() { function loadItemByCode() {
if (!props.value || props.value == '0') { if (!props.value || props.value == '0') {
treeValue.value = []; treeValue.value = [];
} else { } else {
loadDictItem({ ids: props.value }).then(res => { loadDictItem({ ids: props.value }).then((res) => {
let values = props.value.split(','); let values = props.value.split(',');
treeValue.value = res.map((item, index) => ({ treeValue.value = res.map((item, index) => ({
key: values[index], key: values[index],
@ -139,7 +135,6 @@
if (!props.multiple && props.loadTriggleChange) { if (!props.multiple && props.loadTriggleChange) {
backValue(props.value, text); backValue(props.value, text);
} }
} }
function backValue(value, label) { function backValue(value, label) {
@ -163,7 +158,7 @@
pid: pid, pid: pid,
condition: props.condition, condition: props.condition,
}; };
loadTreeData(param).then(res => { loadTreeData(param).then((res) => {
if (res) { if (res) {
for (let i of res) { for (let i of res) {
i.value = i.key; i.value = i.key;
@ -204,7 +199,7 @@
treeValue.value = ''; treeValue.value = '';
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
let labels = []; let labels = [];
let values = value.map(item => { let values = value.map((item) => {
labels.push(item.label); labels.push(item.label);
return item.value; return item.value;
}); });
@ -251,6 +246,5 @@
asyncLoadTreeData, asyncLoadTreeData,
}; };
}, },
}) });
;
</script> </script>

View File

@ -1,83 +1,83 @@
<template> <template>
<a-checkbox-group v-bind="attrs" v-model:value="checkboxArray" :options="checkOptions" @change="handleChange"></a-checkbox-group> <a-checkbox-group v-bind="attrs" v-model:value="checkboxArray" :options="checkOptions" @change="handleChange"></a-checkbox-group>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, computed, watch, watchEffect, ref, unref} from 'vue'; import { defineComponent, computed, watch, watchEffect, ref, unref } from 'vue';
import {propTypes} from "/@/utils/propTypes"; import { propTypes } from '/@/utils/propTypes';
import {useAttrs} from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import {initDictOptions} from "/@/utils/dict/index" import { initDictOptions } from '/@/utils/dict/index';
export default defineComponent({ export default defineComponent({
name: 'JCheckbox', name: 'JCheckbox',
props: { props: {
value: propTypes.string, value: propTypes.string,
dictCode: propTypes.string, dictCode: propTypes.string,
options: { options: {
type: Array, type: Array,
default: () => [], default: () => [],
}, },
}, },
emits: ['change', 'update:value'], emits: ['change', 'update:value'],
setup(props, {emit}) { setup(props, { emit }) {
const attrs = useAttrs(); const attrs = useAttrs();
//checkbox //checkbox
const checkOptions = ref<any[]>([]); const checkOptions = ref<any[]>([]);
//checkbox //checkbox
const checkboxArray = ref<any[]>([]); const checkboxArray = ref<any[]>([]);
/** /**
* 监听value * 监听value
*/ */
watchEffect(() => { watchEffect(() => {
props.value && (checkboxArray.value = props.value ? props.value.split(",") : []); props.value && (checkboxArray.value = props.value ? props.value.split(',') : []);
//update-begin-author:taoyan date:20220401 for: resetFields //update-begin-author:taoyan date:20220401 for: resetFields
if(props.value === '' || props.value === undefined){ if (props.value === '' || props.value === undefined) {
checkboxArray.value = [] checkboxArray.value = [];
} }
//update-end-author:taoyan date:20220401 for: resetFields //update-end-author:taoyan date:20220401 for: resetFields
}); });
/** /**
* 监听字典code * 监听字典code
*/ */
watchEffect(() => { watchEffect(() => {
props && initOptions(); props && initOptions();
}); });
/** /**
* 初始化选项 * 初始化选项
*/ */
async function initOptions() { async function initOptions() {
//options, //options,
if (props.options && props.options.length > 0) { if (props.options && props.options.length > 0) {
checkOptions.value = props.options; checkOptions.value = props.options;
return; return;
} }
//Code, //Code,
if(props.dictCode){ if (props.dictCode) {
const dictData = await initDictOptions(props.dictCode); const dictData = await initDictOptions(props.dictCode);
checkOptions.value = dictData.reduce((prev, next) => { checkOptions.value = dictData.reduce((prev, next) => {
if (next) { if (next) {
const value = next['value']; const value = next['value'];
prev.push({ prev.push({
label: next['text'], label: next['text'],
value: value, value: value,
}); });
}
return prev;
}, []);
}
} }
return prev;
}, []);
}
}
/** /**
* change事件 * change事件
* @param $event * @param $event
*/ */
function handleChange($event) { function handleChange($event) {
emit('update:value', $event.join(',')); emit('update:value', $event.join(','));
emit('change', $event.join(',')); emit('change', $event.join(','));
} }
return {checkboxArray, checkOptions, attrs, handleChange}; return { checkboxArray, checkOptions, attrs, handleChange };
}, },
}); });
</script> </script>

View File

@ -1,17 +1,17 @@
<template> <template>
<div v-bind="boxBindProps"> <div v-bind="boxBindProps">
<!-- 全屏按钮 --> <!-- 全屏按钮 -->
<a-icon v-if="fullScreen" class="full-screen-icon" :type="fullScreenIcon" @click="onToggleFullScreen"/> <a-icon v-if="fullScreen" class="full-screen-icon" :type="fullScreenIcon" @click="onToggleFullScreen" />
<textarea ref="textarea" v-bind="getBindValue"></textarea> <textarea ref="textarea" v-bind="getBindValue"></textarea>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, onMounted, reactive, ref, watch, unref, computed} from 'vue'; import { defineComponent, onMounted, reactive, ref, watch, unref, computed } from 'vue';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { useRuleFormItem } from '/@/hooks/component/useFormItem'; import { useRuleFormItem } from '/@/hooks/component/useFormItem';
// //
import _CodeMirror, { EditorFromTextArea } from 'codemirror' import _CodeMirror, { EditorFromTextArea } from 'codemirror';
// //
import 'codemirror/lib/codemirror.css'; import 'codemirror/lib/codemirror.css';
// options // options
@ -29,22 +29,22 @@
import 'codemirror/mode/swift/swift.js'; import 'codemirror/mode/swift/swift.js';
import 'codemirror/mode/vue/vue.js'; import 'codemirror/mode/vue/vue.js';
// : // :
import "codemirror/addon/fold/foldgutter.css"; import 'codemirror/addon/fold/foldgutter.css';
import "codemirror/addon/fold/foldcode.js"; import 'codemirror/addon/fold/foldcode.js';
import "codemirror/addon/fold/brace-fold.js"; import 'codemirror/addon/fold/brace-fold.js';
import "codemirror/addon/fold/comment-fold.js"; import 'codemirror/addon/fold/comment-fold.js';
import "codemirror/addon/fold/indent-fold.js"; import 'codemirror/addon/fold/indent-fold.js';
import "codemirror/addon/fold/foldgutter.js"; import 'codemirror/addon/fold/foldgutter.js';
// : // :
//styleActiveLinetrue //styleActiveLinetrue
import "codemirror/addon/selection/active-line.js"; import 'codemirror/addon/selection/active-line.js';
// //
import "codemirror/addon/hint/show-hint.css"; import 'codemirror/addon/hint/show-hint.css';
import "codemirror/addon/hint/show-hint.js"; import 'codemirror/addon/hint/show-hint.js';
import "codemirror/addon/hint/anyword-hint.js"; import 'codemirror/addon/hint/anyword-hint.js';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import {useDesign} from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign';
import {isJsonObjectString} from '/@/utils/is.ts' import { isJsonObjectString } from '/@/utils/is.ts';
export default defineComponent({ export default defineComponent({
name: 'JCodeEditor', name: 'JCodeEditor',
@ -53,7 +53,7 @@
components: {}, components: {},
props: { props: {
value: propTypes.string.def(''), value: propTypes.string.def(''),
height:propTypes.string.def('auto'), height: propTypes.string.def('auto'),
disabled: propTypes.bool.def(false), disabled: propTypes.bool.def(false),
// //
fullScreen: propTypes.bool.def(false), fullScreen: propTypes.bool.def(false),
@ -68,62 +68,67 @@
// //
const [state] = useRuleFormItem(props, 'value', 'change', emitData); const [state] = useRuleFormItem(props, 'value', 'change', emitData);
const textarea = ref<HTMLTextAreaElement>(); const textarea = ref<HTMLTextAreaElement>();
let coder: Nullable<EditorFromTextArea> = null let coder: Nullable<EditorFromTextArea> = null;
const attrs = useAttrs(); const attrs = useAttrs();
const height =ref(props.height); const height = ref(props.height);
const options = reactive({ const options = reactive({
// //
tabSize: 2, tabSize: 2,
// JS // JS
theme: 'cobalt', theme: 'cobalt',
smartIndent: true, // smartIndent: true, //
// //
lineNumbers: true, lineNumbers: true,
line: true, line: true,
// : // :
foldGutter: true, foldGutter: true,
lineWrapping: true, lineWrapping: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
// : // :
// //
styleActiveLine: true, styleActiveLine: true,
// //
extraKeys:{ extraKeys: {
Tab: function autoFormat(editor) { Tab: function autoFormat(editor) {
//var totalLines = editor.lineCount(); //var totalLines = editor.lineCount();
//editor.autoFormatRange({line:0, ch:0}, {line:totalLines}); //editor.autoFormatRange({line:0, ch:0}, {line:totalLines});
setValue(innerValue,false) setValue(innerValue, false);
} },
} },
}); });
let innerValue = '' let innerValue = '';
// //
const isFullScreen = ref(false) const isFullScreen = ref(false);
const fullScreenIcon = computed(() => isFullScreen.value ? 'fullscreen-exit' : 'fullscreen') const fullScreenIcon = computed(() => (isFullScreen.value ? 'fullscreen-exit' : 'fullscreen'));
// //
const boxBindProps = computed(() => { const boxBindProps = computed(() => {
let _props = { let _props = {
class: [ class: [
prefixCls, 'full-screen-parent', 'auto-height', prefixCls,
'full-screen-parent',
'auto-height',
{ {
'full-screen': isFullScreen.value, 'full-screen': isFullScreen.value,
}, },
], ],
style: {}, style: {},
} };
if (isFullScreen.value) { if (isFullScreen.value) {
_props.style['z-index'] = props.zIndex _props.style['z-index'] = props.zIndex;
} }
return _props return _props;
}) });
/** /**
* 监听组件值 * 监听组件值
*/ */
watch(() => props.value, () => { watch(
if (innerValue != props.value) { () => props.value,
setValue(props.value, false); () => {
if (innerValue != props.value) {
setValue(props.value, false);
}
} }
}); );
onMounted(() => { onMounted(() => {
initialize(); initialize();
}); });
@ -134,12 +139,12 @@
* @param trigger 是否触发 change 事件 * @param trigger 是否触发 change 事件
*/ */
function setValue(value: string, trigger = true) { function setValue(value: string, trigger = true) {
if(value && isJsonObjectString(value)){ if (value && isJsonObjectString(value)) {
value = JSON.stringify(JSON.parse(value),null,2); value = JSON.stringify(JSON.parse(value), null, 2);
} }
coder?.setValue(value ?? '') coder?.setValue(value ?? '');
innerValue = value innerValue = value;
trigger && emitChange(innerValue) trigger && emitChange(innerValue);
} }
// //
@ -147,7 +152,7 @@
let value = obj.getValue(); let value = obj.getValue();
innerValue = value || ''; innerValue = value || '';
if (props.value != innerValue) { if (props.value != innerValue) {
emitChange(innerValue) emitChange(innerValue);
} }
} }
@ -162,12 +167,12 @@
// //
coder.on('change', onChange); coder.on('change', onChange);
// //
setValue(innerValue, false) setValue(innerValue, false);
} }
// //
function onToggleFullScreen() { function onToggleFullScreen() {
isFullScreen.value = !isFullScreen.value isFullScreen.value = !isFullScreen.value;
} }
const getBindValue = Object.assign({}, unref(props), unref(attrs)); const getBindValue = Object.assign({}, unref(props), unref(attrs));
@ -186,74 +191,72 @@
</script> </script>
<style lang="less"> <style lang="less">
//noinspection LessUnresolvedVariable //noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-code-editer'; @prefix-cls: ~'@{namespace}-code-editer';
.@{prefix-cls} { .@{prefix-cls} {
&.auto-height {
&.auto-height { .CodeMirror {
.CodeMirror { height: v-bind(height) !important;
height: v-bind(height) !important; min-height: 100px;
min-height: 100px; }
}
}
/* 全屏样式 */
&.full-screen-parent {
position: relative;
.full-screen-icon {
opacity: 0;
color: black;
width: 20px;
height: 20px;
line-height: 24px;
background-color: white;
position: absolute;
top: 2px;
right: 2px;
z-index: 9;
cursor: pointer;
transition: opacity 0.3s;
padding: 2px 0 0 1.5px;
} }
&:hover { /* 全屏样式 */
&.full-screen-parent {
position: relative;
.full-screen-icon { .full-screen-icon {
opacity: 1; opacity: 0;
color: black;
width: 20px;
height: 20px;
line-height: 24px;
background-color: white;
position: absolute;
top: 2px;
right: 2px;
z-index: 9;
cursor: pointer;
transition: opacity 0.3s;
padding: 2px 0 0 1.5px;
}
&:hover { &:hover {
background-color: rgba(255, 255, 255, 0.88); .full-screen-icon {
opacity: 1;
&:hover {
background-color: rgba(255, 255, 255, 0.88);
}
} }
} }
}
&.full-screen { &.full-screen {
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
padding: 8px; padding: 8px;
background-color: #f5f5f5; background-color: #f5f5f5;
.full-screen-icon { .full-screen-icon {
top: 12px; top: 12px;
right: 12px; right: 12px;
}
.full-screen-child,
.CodeMirror {
height: 100%;
max-height: 100%;
min-height: 100%;
}
} }
.full-screen-child, .full-screen-child {
.CodeMirror {
height: 100%; height: 100%;
max-height: 100%;
min-height: 100%;
} }
} }
.full-screen-child {
height: 100%;
}
} }
}
</style> </style>

View File

@ -1,164 +1,161 @@
<template> <template>
<a-radio-group v-if="compType===CompTypeEnum.Radio" v-bind="attrs" v-model:value="state" @change="handleChange"> <a-radio-group v-if="compType === CompTypeEnum.Radio" v-bind="attrs" v-model:value="state" @change="handleChange">
<template v-for="item in dictOptions" :key="`${item.value}`"> <template v-for="item in dictOptions" :key="`${item.value}`">
<a-radio :value="item.value"> <a-radio :value="item.value">
{{ item.label }} {{ item.label }}
</a-radio> </a-radio>
</template> </template>
</a-radio-group> </a-radio-group>
<a-radio-group v-else-if="compType===CompTypeEnum.RadioButton" v-bind="attrs" v-model:value="state" buttonStyle="solid" @change="handleChange"> <a-radio-group v-else-if="compType === CompTypeEnum.RadioButton" v-bind="attrs" v-model:value="state" buttonStyle="solid" @change="handleChange">
<template v-for="item in dictOptions" :key="`${item.value}`"> <template v-for="item in dictOptions" :key="`${item.value}`">
<a-radio-button :value="item.value"> <a-radio-button :value="item.value">
{{ item.label }} {{ item.label }}
</a-radio-button> </a-radio-button>
</template> </template>
</a-radio-group> </a-radio-group>
<template v-else-if="compType===CompTypeEnum.Select"> <template v-else-if="compType === CompTypeEnum.Select">
<!-- 显示加载效果 --> <!-- 显示加载效果 -->
<a-input v-if="loadingEcho" readOnly placeholder="加载中…"> <a-input v-if="loadingEcho" readOnly placeholder="加载中…">
<template #prefix> <template #prefix>
<LoadingOutlined/> <LoadingOutlined />
</template> </template>
</a-input> </a-input>
<a-select v-else :placeholder="placeholder" v-bind="attrs" v-model:value="state" :filterOption="handleFilterOption" :getPopupContainer="getPopupContainer" @change="handleChange"> <a-select v-else :placeholder="placeholder" v-bind="attrs" v-model:value="state" :filterOption="handleFilterOption" :getPopupContainer="getPopupContainer" @change="handleChange">
<a-select-option v-if="showChooseOption" :value="undefined"></a-select-option> <a-select-option v-if="showChooseOption" :value="undefined"></a-select-option>
<template v-for="item in dictOptions" :key="`${item.value}`"> <template v-for="item in dictOptions" :key="`${item.value}`">
<a-select-option :value="item.value"> <a-select-option :value="item.value">
<span style="display: inline-block;width: 100%" :title=" item.label "> <span style="display: inline-block; width: 100%" :title="item.label">
{{ item.label }} {{ item.label }}
</span> </span>
</a-select-option> </a-select-option>
</template> </template>
</a-select> </a-select>
</template> </template>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted} from 'vue'; import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted } from 'vue';
import {propTypes} from "/@/utils/propTypes"; import { propTypes } from '/@/utils/propTypes';
import {useAttrs} from "/@/hooks/core/useAttrs"; import { useAttrs } from '/@/hooks/core/useAttrs';
import {initDictOptions} from "/@/utils/dict/index" import { initDictOptions } from '/@/utils/dict/index';
import {get, omit} from 'lodash-es'; import { get, omit } from 'lodash-es';
import {useRuleFormItem} from '/@/hooks/component/useFormItem'; import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import {CompTypeEnum} from '/@/enums/CompTypeEnum.ts'; import { CompTypeEnum } from '/@/enums/CompTypeEnum.ts';
import {LoadingOutlined} from '@ant-design/icons-vue' import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({ export default defineComponent({
name: 'JDictSelectTag', name: 'JDictSelectTag',
inheritAttrs: false, inheritAttrs: false,
components: {LoadingOutlined}, components: { LoadingOutlined },
props: { props: {
value: propTypes.oneOfType([ value: propTypes.oneOfType([propTypes.string, propTypes.number, propTypes.array]),
propTypes.string, dictCode: propTypes.string,
propTypes.number, type: propTypes.string,
propTypes.array, placeholder: propTypes.string,
]), stringToNumber: propTypes.bool,
dictCode: propTypes.string, getPopupContainer: {
type: propTypes.string, type: Function,
placeholder: propTypes.string, default: (node) => node.parentNode,
stringToNumber: propTypes.bool, },
getPopupContainer: { //
type: Function, showChooseOption: propTypes.bool.def(true),
default: (node) => node.parentNode // -online使
}, options: {
// type: Array,
showChooseOption: propTypes.bool.def(true), default: [],
// -online使 required: false,
options: { },
type: Array, },
default: [], emits: ['options-change', 'change'],
required: false setup(props, { emit, refs }) {
}, const emitData = ref<any[]>([]);
}, const dictOptions = ref<any[]>([]);
emits: ['options-change', 'change'], const attrs = useAttrs();
setup(props, {emit, refs}) { const [state] = useRuleFormItem(props, 'value', 'change', emitData);
const emitData = ref<any[]>([]); const getBindValue = Object.assign({}, unref(props), unref(attrs));
const dictOptions = ref<any[]>([]); //
const attrs = useAttrs(); const loadingEcho = ref<boolean>(false);
const [state] = useRuleFormItem(props, 'value', 'change', emitData); // loading
const getBindValue = Object.assign({}, unref(props), unref(attrs)); let isFirstLoadEcho = true;
//
const loadingEcho = ref<boolean>(false)
// loading
let isFirstLoadEcho = true
// //
const compType = computed(() => { const compType = computed(() => {
return (!props.type || props.type === "list") ? 'select' : props.type; return !props.type || props.type === 'list' ? 'select' : props.type;
}); });
/** /**
* 监听字典code * 监听字典code
*/ */
watchEffect(() => { watchEffect(() => {
if (props.dictCode) { if (props.dictCode) {
loadingEcho.value = isFirstLoadEcho loadingEcho.value = isFirstLoadEcho;
isFirstLoadEcho = false isFirstLoadEcho = false;
initDictData().finally(() => { initDictData().finally(() => {
loadingEcho.value = isFirstLoadEcho loadingEcho.value = isFirstLoadEcho;
}) });
} }
//update-begin-author:taoyan date: dictCode options-- //update-begin-author:taoyan date: dictCode options--
if(!props.dictCode){ if (!props.dictCode) {
dictOptions.value = props.options dictOptions.value = props.options;
} }
//update-end-author:taoyan date: dictCode options-- //update-end-author:taoyan date: dictCode options--
});
});
//update-begin-author:taoyan date:20220404 for: 使useRuleFormItemvaluechange
// change,value''change
watch(()=>props.value, ()=>{
if(props.value===''){
emit('change', '')
}
});
//update-end-author:taoyan date:20220404 for: 使useRuleFormItemvaluechange
async function initDictData() { //update-begin-author:taoyan date:20220404 for: 使useRuleFormItemvaluechange
let {dictCode, stringToNumber} = props; // change,value''change
//Code, watch(
const dictData = await initDictOptions(dictCode); () => props.value,
dictOptions.value = dictData.reduce((prev, next) => { () => {
if (next) { if (props.value === '') {
const value = next['value']; emit('change', '');
prev.push({
label: next['text'] || next['label'],
value: stringToNumber ? +value : value,
...omit(next, ['text', 'value']),
});
}
return prev;
}, []);
}
function handleChange(e) {
emitData.value = [e?.target?.value || e];
} }
}
);
//update-end-author:taoyan date:20220404 for: 使useRuleFormItemvaluechange
/** 用于搜索下拉框中的内容 */ async function initDictData() {
function handleFilterOption(input, option) { let { dictCode, stringToNumber } = props;
// label //Code,
let labelIf = option?.children[0]?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 const dictData = await initDictOptions(dictCode);
if (labelIf) { dictOptions.value = dictData.reduce((prev, next) => {
return true if (next) {
} const value = next['value'];
// value prev.push({
return (option.value || '').toString().toLowerCase().indexOf(input.toLowerCase()) >= 0 label: next['text'] || next['label'],
value: stringToNumber ? +value : value,
...omit(next, ['text', 'value']),
});
} }
return prev;
}, []);
}
return { function handleChange(e) {
state, emitData.value = [e?.target?.value || e];
compType, }
attrs,
loadingEcho, /** 用于搜索下拉框中的内容 */
getBindValue, function handleFilterOption(input, option) {
dictOptions, // label
CompTypeEnum, let labelIf = option?.children[0]?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0;
handleChange, if (labelIf) {
handleFilterOption, return true;
}; }
}, // value
}); return (option.value || '').toString().toLowerCase().indexOf(input.toLowerCase()) >= 0;
}
return {
state,
compType,
attrs,
loadingEcho,
getBindValue,
dictOptions,
CompTypeEnum,
handleChange,
handleFilterOption,
};
},
});
</script> </script>

View File

@ -3,78 +3,78 @@
<div class="content"> <div class="content">
<a-tabs :size="`small`" v-model:activeKey="activeKey"> <a-tabs :size="`small`" v-model:activeKey="activeKey">
<a-tab-pane tab="秒" key="second" v-if="!hideSecond"> <a-tab-pane tab="秒" key="second" v-if="!hideSecond">
<SecondUI v-model:value="second" :disabled="disabled"/> <SecondUI v-model:value="second" :disabled="disabled" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane tab="分" key="minute"> <a-tab-pane tab="分" key="minute">
<MinuteUI v-model:value="minute" :disabled="disabled"/> <MinuteUI v-model:value="minute" :disabled="disabled" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane tab="时" key="hour"> <a-tab-pane tab="时" key="hour">
<HourUI v-model:value="hour" :disabled="disabled"/> <HourUI v-model:value="hour" :disabled="disabled" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane tab="日" key="day"> <a-tab-pane tab="日" key="day">
<DayUI v-model:value="day" :week="week" :disabled="disabled"/> <DayUI v-model:value="day" :week="week" :disabled="disabled" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane tab="月" key="month"> <a-tab-pane tab="月" key="month">
<MonthUI v-model:value="month" :disabled="disabled"/> <MonthUI v-model:value="month" :disabled="disabled" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane tab="周" key="week"> <a-tab-pane tab="周" key="week">
<WeekUI v-model:value="week" :day="day" :disabled="disabled"/> <WeekUI v-model:value="week" :day="day" :disabled="disabled" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane tab="年" key="year" v-if="!hideYear && !hideSecond"> <a-tab-pane tab="年" key="year" v-if="!hideYear && !hideSecond">
<YearUI v-model:value="year" :disabled="disabled"/> <YearUI v-model:value="year" :disabled="disabled" />
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
<a-divider/> <a-divider />
<!-- 执行时间预览 --> <!-- 执行时间预览 -->
<a-row :gutter="8"> <a-row :gutter="8">
<a-col :span="18" style="margin-top: 22px;"> <a-col :span="18" style="margin-top: 22px">
<a-row :gutter="8"> <a-row :gutter="8">
<a-col :span="8" style="margin-bottom: 12px;"> <a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.second" @blur="onInputBlur"> <a-input v-model:value="inputValues.second" @blur="onInputBlur">
<template #addonBefore> <template #addonBefore>
<span class="allow-click" @click="activeKey='second'"></span> <span class="allow-click" @click="activeKey = 'second'"></span>
</template> </template>
</a-input> </a-input>
</a-col> </a-col>
<a-col :span="8" style="margin-bottom: 12px;"> <a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.minute" @blur="onInputBlur"> <a-input v-model:value="inputValues.minute" @blur="onInputBlur">
<template #addonBefore> <template #addonBefore>
<span class="allow-click" @click="activeKey='minute'"></span> <span class="allow-click" @click="activeKey = 'minute'"></span>
</template> </template>
</a-input> </a-input>
</a-col> </a-col>
<a-col :span="8" style="margin-bottom: 12px;"> <a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.hour" @blur="onInputBlur"> <a-input v-model:value="inputValues.hour" @blur="onInputBlur">
<template #addonBefore> <template #addonBefore>
<span class="allow-click" @click="activeKey='hour'"></span> <span class="allow-click" @click="activeKey = 'hour'"></span>
</template> </template>
</a-input> </a-input>
</a-col> </a-col>
<a-col :span="8" style="margin-bottom: 12px;"> <a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.day" @blur="onInputBlur"> <a-input v-model:value="inputValues.day" @blur="onInputBlur">
<template #addonBefore> <template #addonBefore>
<span class="allow-click" @click="activeKey='day'"></span> <span class="allow-click" @click="activeKey = 'day'"></span>
</template> </template>
</a-input> </a-input>
</a-col> </a-col>
<a-col :span="8" style="margin-bottom: 12px;"> <a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.month" @blur="onInputBlur"> <a-input v-model:value="inputValues.month" @blur="onInputBlur">
<template #addonBefore> <template #addonBefore>
<span class="allow-click" @click="activeKey='month'"></span> <span class="allow-click" @click="activeKey = 'month'"></span>
</template> </template>
</a-input> </a-input>
</a-col> </a-col>
<a-col :span="8" style="margin-bottom: 12px;"> <a-col :span="8" style="margin-bottom: 12px">
<a-input v-model:value="inputValues.week" @blur="onInputBlur"> <a-input v-model:value="inputValues.week" @blur="onInputBlur">
<template #addonBefore> <template #addonBefore>
<span class="allow-click" @click="activeKey='week'"></span> <span class="allow-click" @click="activeKey = 'week'"></span>
</template> </template>
</a-input> </a-input>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-input v-model:value="inputValues.year" @blur="onInputBlur"> <a-input v-model:value="inputValues.year" @blur="onInputBlur">
<template #addonBefore> <template #addonBefore>
<span class="allow-click" @click="activeKey='year'"></span> <span class="allow-click" @click="activeKey = 'year'"></span>
</template> </template>
</a-input> </a-input>
</a-col> </a-col>
@ -89,7 +89,7 @@
</a-col> </a-col>
<a-col :span="6"> <a-col :span="6">
<div>近十次执行时间不含年</div> <div>近十次执行时间不含年</div>
<a-textarea type="textarea" :value="preTimeList" :rows="5"/> <a-textarea type="textarea" :value="preTimeList" :rows="5" />
</a-col> </a-col>
</a-row> </a-row>
</div> </div>
@ -97,209 +97,214 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, reactive, ref, watch, provide } from 'vue' import { computed, reactive, ref, watch, provide } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign';
import CronParser from 'cron-parser' import CronParser from 'cron-parser';
import SecondUI from './tabs/SecondUI.vue' import SecondUI from './tabs/SecondUI.vue';
import MinuteUI from './tabs/MinuteUI.vue' import MinuteUI from './tabs/MinuteUI.vue';
import HourUI from './tabs/HourUI.vue' import HourUI from './tabs/HourUI.vue';
import DayUI from './tabs/DayUI.vue' import DayUI from './tabs/DayUI.vue';
import MonthUI from './tabs/MonthUI.vue' import MonthUI from './tabs/MonthUI.vue';
import WeekUI from './tabs/WeekUI.vue' import WeekUI from './tabs/WeekUI.vue';
import YearUI from './tabs/YearUI.vue' import YearUI from './tabs/YearUI.vue';
import { cronEmits, cronProps } from './easy.cron.data' import { cronEmits, cronProps } from './easy.cron.data';
import { dateFormat, simpleDebounce } from '/@/utils/common/compUtils' import { dateFormat, simpleDebounce } from '/@/utils/common/compUtils';
const { prefixCls } = useDesign('easy-cron-inner') const { prefixCls } = useDesign('easy-cron-inner');
provide('prefixCls', prefixCls) provide('prefixCls', prefixCls);
const emit = defineEmits([...cronEmits]) const emit = defineEmits([...cronEmits]);
const props = defineProps({ ...cronProps }) const props = defineProps({ ...cronProps });
const activeKey = ref(props.hideSecond ? 'minute' : 'second') const activeKey = ref(props.hideSecond ? 'minute' : 'second');
const second = ref('*') const second = ref('*');
const minute = ref('*') const minute = ref('*');
const hour = ref('*') const hour = ref('*');
const day = ref('*') const day = ref('*');
const month = ref('*') const month = ref('*');
const week = ref('?') const week = ref('?');
const year = ref('*') const year = ref('*');
const inputValues = reactive({ second: '', minute: '', hour: '', day: '', month: '', week: '', year: '', cron: '' }) const inputValues = reactive({ second: '', minute: '', hour: '', day: '', month: '', week: '', year: '', cron: '' });
const preTimeList = ref('执行预览,会忽略年份参数。') const preTimeList = ref('执行预览,会忽略年份参数。');
// cron // cron
const cronValueInner = computed(() => { const cronValueInner = computed(() => {
let result: string[] = [] let result: string[] = [];
if (!props.hideSecond) { if (!props.hideSecond) {
result.push(second.value ? second.value : '*') result.push(second.value ? second.value : '*');
}
result.push(minute.value ? minute.value : '*')
result.push(hour.value ? hour.value : '*')
result.push(day.value ? day.value : '*')
result.push(month.value ? month.value : '*')
result.push(week.value ? week.value : '?')
if (!props.hideYear && !props.hideSecond) result.push(year.value ? year.value : '*')
return result.join(' ')
})
//
const cronValueNoYear = computed(() => {
const v = cronValueInner.value
if (props.hideYear || props.hideSecond) return v
const vs = v.split(' ')
if (vs.length >= 6) {
// Quartz
vs[5] = convertWeekToQuartz(vs[5])
}
return vs.slice(0, vs.length - 1).join(' ')
})
const calTriggerList = simpleDebounce(calTriggerListInner, 500)
watch(() => props.value, (newVal) => {
if (newVal === cronValueInner.value) {
return
}
formatValue()
})
watch(cronValueInner, (newValue) => {
calTriggerList()
emitValue(newValue)
assignInput()
})
// watch(minute, () => {
// if (second.value === '*') {
// second.value = '0'
// }
// })
// watch(hour, () => {
// if (minute.value === '*') {
// minute.value = '0'
// }
// })
// watch(day, () => {
// if (day.value !== '?' && hour.value === '*') {
// hour.value = '0'
// }
// })
// watch(week, () => {
// if (week.value !== '?' && hour.value === '*') {
// hour.value = '0'
// }
// })
// watch(month, () => {
// if (day.value === '?' && week.value === '*') {
// week.value = '1'
// } else if (week.value === '?' && day.value === '*') {
// day.value = '1'
// }
// })
// watch(year, () => {
// if (month.value === '*') {
// month.value = '1'
// }
// })
assignInput()
formatValue()
calTriggerListInner()
function assignInput() {
inputValues.second = second.value
inputValues.minute = minute.value
inputValues.hour = hour.value
inputValues.day = day.value
inputValues.month = month.value
inputValues.week = week.value
inputValues.year = year.value
inputValues.cron = cronValueInner.value
}
function formatValue() {
if (!props.value) return
const values = props.value.split(' ').filter(item => !!item)
if (!values || values.length <= 0) return
let i = 0
if (!props.hideSecond) second.value = values[i++]
if (values.length > i) minute.value = values[i++]
if (values.length > i) hour.value = values[i++]
if (values.length > i) day.value = values[i++]
if (values.length > i) month.value = values[i++]
if (values.length > i) week.value = values[i++]
if (values.length > i) year.value = values[i]
assignInput()
}
// Quartz
// 1 = 2 = 3 = 4 = 5 = 6 = 7 =
function convertWeekToQuartz(week: string) {
let convert = (v: string) => {
if (v === '0') {
return '1'
} }
if (v === '1') { result.push(minute.value ? minute.value : '*');
return '0' result.push(hour.value ? hour.value : '*');
result.push(day.value ? day.value : '*');
result.push(month.value ? month.value : '*');
result.push(week.value ? week.value : '?');
if (!props.hideYear && !props.hideSecond) result.push(year.value ? year.value : '*');
return result.join(' ');
});
//
const cronValueNoYear = computed(() => {
const v = cronValueInner.value;
if (props.hideYear || props.hideSecond) return v;
const vs = v.split(' ');
if (vs.length >= 6) {
// Quartz
vs[5] = convertWeekToQuartz(vs[5]);
} }
return (Number.parseInt(v) - 1).toString() return vs.slice(0, vs.length - 1).join(' ');
} });
// 1-7 or 1/7 const calTriggerList = simpleDebounce(calTriggerListInner, 500);
let patten1 = /^([0-7])([-/])([0-7])$/
// 1,4,7 watch(
let patten2 = /^([0-7])(,[0-7])+$/ () => props.value,
if (/^[0-7]$/.test(week)) { (newVal) => {
return convert(week) if (newVal === cronValueInner.value) {
} else if (patten1.test(week)) { return;
return week.replace(patten1, ($0, before, separator, after) => {
if (separator === '/') {
return convert(before) + separator + after
} else {
return convert(before) + separator + convert(after)
} }
}) formatValue();
} else if (patten2.test(week)) { }
return week.split(',').map(v => convert(v)).join(',') );
}
return week
}
function calTriggerListInner() { watch(cronValueInner, (newValue) => {
// calTriggerList();
if (props.remote) { emitValue(newValue);
props.remote(cronValueInner.value, +new Date(), v => { assignInput();
preTimeList.value = v });
})
return
}
const format = 'yyyy-MM-dd hh:mm:ss'
const options = {
currentDate: dateFormat(new Date(), format),
}
const iter = CronParser.parseExpression(cronValueNoYear.value, options)
const result: string[] = []
for (let i = 1; i <= 10; i++) {
result.push(dateFormat(new Date(iter.next() as any), format))
}
preTimeList.value = result.length > 0 ? result.join('\n') : '无执行时间'
}
function onInputBlur() { // watch(minute, () => {
second.value = inputValues.second // if (second.value === '*') {
minute.value = inputValues.minute // second.value = '0'
hour.value = inputValues.hour // }
day.value = inputValues.day // })
month.value = inputValues.month // watch(hour, () => {
week.value = inputValues.week // if (minute.value === '*') {
year.value = inputValues.year // minute.value = '0'
} // }
// })
// watch(day, () => {
// if (day.value !== '?' && hour.value === '*') {
// hour.value = '0'
// }
// })
// watch(week, () => {
// if (week.value !== '?' && hour.value === '*') {
// hour.value = '0'
// }
// })
// watch(month, () => {
// if (day.value === '?' && week.value === '*') {
// week.value = '1'
// } else if (week.value === '?' && day.value === '*') {
// day.value = '1'
// }
// })
// watch(year, () => {
// if (month.value === '*') {
// month.value = '1'
// }
// })
function onInputCronBlur(event) { assignInput();
emitValue(event.target.value) formatValue();
} calTriggerListInner();
function emitValue(value) { function assignInput() {
emit('change', value) inputValues.second = second.value;
emit('update:value', value) inputValues.minute = minute.value;
} inputValues.hour = hour.value;
inputValues.day = day.value;
inputValues.month = month.value;
inputValues.week = week.value;
inputValues.year = year.value;
inputValues.cron = cronValueInner.value;
}
function formatValue() {
if (!props.value) return;
const values = props.value.split(' ').filter((item) => !!item);
if (!values || values.length <= 0) return;
let i = 0;
if (!props.hideSecond) second.value = values[i++];
if (values.length > i) minute.value = values[i++];
if (values.length > i) hour.value = values[i++];
if (values.length > i) day.value = values[i++];
if (values.length > i) month.value = values[i++];
if (values.length > i) week.value = values[i++];
if (values.length > i) year.value = values[i];
assignInput();
}
// Quartz
// 1 = 2 = 3 = 4 = 5 = 6 = 7 =
function convertWeekToQuartz(week: string) {
let convert = (v: string) => {
if (v === '0') {
return '1';
}
if (v === '1') {
return '0';
}
return (Number.parseInt(v) - 1).toString();
};
// 1-7 or 1/7
let patten1 = /^([0-7])([-/])([0-7])$/;
// 1,4,7
let patten2 = /^([0-7])(,[0-7])+$/;
if (/^[0-7]$/.test(week)) {
return convert(week);
} else if (patten1.test(week)) {
return week.replace(patten1, ($0, before, separator, after) => {
if (separator === '/') {
return convert(before) + separator + after;
} else {
return convert(before) + separator + convert(after);
}
});
} else if (patten2.test(week)) {
return week
.split(',')
.map((v) => convert(v))
.join(',');
}
return week;
}
function calTriggerListInner() {
//
if (props.remote) {
props.remote(cronValueInner.value, +new Date(), (v) => {
preTimeList.value = v;
});
return;
}
const format = 'yyyy-MM-dd hh:mm:ss';
const options = {
currentDate: dateFormat(new Date(), format),
};
const iter = CronParser.parseExpression(cronValueNoYear.value, options);
const result: string[] = [];
for (let i = 1; i <= 10; i++) {
result.push(dateFormat(new Date(iter.next() as any), format));
}
preTimeList.value = result.length > 0 ? result.join('\n') : '无执行时间';
}
function onInputBlur() {
second.value = inputValues.second;
minute.value = inputValues.minute;
hour.value = inputValues.hour;
day.value = inputValues.day;
month.value = inputValues.month;
week.value = inputValues.week;
year.value = inputValues.year;
}
function onInputCronBlur(event) {
emitValue(event.target.value);
}
function emitValue(value) {
emit('change', value);
emit('update:value', value);
}
</script> </script>
<style lang="less"> <style lang="less">
@import "easy.cron.inner"; @import 'easy.cron.inner';
</style> </style>

View File

@ -2,62 +2,55 @@
<div :class="`${prefixCls}`"> <div :class="`${prefixCls}`">
<a-input :placeholder="placeholder" v-model:value="editCronValue" :disabled="disabled"> <a-input :placeholder="placeholder" v-model:value="editCronValue" :disabled="disabled">
<template #addonAfter> <template #addonAfter>
<a class="open-btn" :disabled="disabled?'disabled':null" @click="showConfigModal"> <a class="open-btn" :disabled="disabled ? 'disabled' : null" @click="showConfigModal">
<Icon icon="ant-design:setting-outlined"/> <Icon icon="ant-design:setting-outlined" />
<span>选择</span> <span>选择</span>
</a> </a>
</template> </template>
</a-input> </a-input>
<EasyCronModal <EasyCronModal @register="registerModal" v-model:value="editCronValue" :exeStartTime="exeStartTime" :hideYear="hideYear" :remote="remote" :hideSecond="hideSecond" />
@register="registerModal"
v-model:value="editCronValue"
:exeStartTime="exeStartTime"
:hideYear="hideYear"
:remote="remote"
:hideSecond="hideSecond"/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch } from 'vue' import { ref, watch } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign';
import { useModal } from '/@/components/Modal' import { useModal } from '/@/components/Modal';
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes';
import Icon from '/@/components/Icon/src/Icon.vue' import Icon from '/@/components/Icon/src/Icon.vue';
import EasyCronModal from './EasyCronModal.vue' import EasyCronModal from './EasyCronModal.vue';
import { cronEmits, cronProps } from './easy.cron.data' import { cronEmits, cronProps } from './easy.cron.data';
const { prefixCls } = useDesign('easy-cron-input') const { prefixCls } = useDesign('easy-cron-input');
const emit = defineEmits([...cronEmits]) const emit = defineEmits([...cronEmits]);
const props = defineProps({ const props = defineProps({
...cronProps, ...cronProps,
placeholder: propTypes.string.def('请输入cron表达式'), placeholder: propTypes.string.def('请输入cron表达式'),
exeStartTime: propTypes.oneOfType([ exeStartTime: propTypes.oneOfType([propTypes.number, propTypes.string, propTypes.object]).def(0),
propTypes.number, });
propTypes.string, const [registerModal, { openModal }] = useModal();
propTypes.object, const editCronValue = ref(props.value);
]).def(0),
})
const [registerModal, { openModal }] = useModal()
const editCronValue = ref(props.value)
watch(() => props.value, (newVal) => { watch(
if (newVal !== editCronValue.value) { () => props.value,
editCronValue.value = newVal (newVal) => {
if (newVal !== editCronValue.value) {
editCronValue.value = newVal;
}
}
);
watch(editCronValue, (newVal) => {
emit('change', newVal);
emit('update:value', newVal);
});
function showConfigModal() {
if (!props.disabled) {
openModal();
}
} }
})
watch(editCronValue, (newVal) => {
emit('change', newVal)
emit('update:value', newVal)
})
function showConfigModal() {
if (!props.disabled) {
openModal()
}
}
</script> </script>
<style lang="less"> <style lang="less">
@import "easy.cron.input"; @import 'easy.cron.input';
</style> </style>

View File

@ -1,28 +1,28 @@
<template> <template>
<BasicModal @register="registerModal" title="Cron表达式" width="800px" @ok="onOk"> <BasicModal @register="registerModal" title="Cron表达式" width="800px" @ok="onOk">
<EasyCron v-bind="attrs"/> <EasyCron v-bind="attrs" />
</BasicModal> </BasicModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
import { useAttrs } from '/@/hooks/core/useAttrs' import { useAttrs } from '/@/hooks/core/useAttrs';
import { BasicModal, useModalInner } from '/@/components/Modal' import { BasicModal, useModalInner } from '/@/components/Modal';
import EasyCron from './EasyCronInner.vue' import EasyCron from './EasyCronInner.vue';
export default defineComponent({ export default defineComponent({
name: 'EasyCronModal', name: 'EasyCronModal',
inheritAttrs: false, inheritAttrs: false,
components: { BasicModal, EasyCron }, components: { BasicModal, EasyCron },
setup() { setup() {
const attrs = useAttrs() const attrs = useAttrs();
const [registerModal, { closeModal }] = useModalInner() const [registerModal, { closeModal }] = useModalInner();
function onOk() { function onOk() {
closeModal() closeModal();
} }
return { attrs, registerModal, onOk } return { attrs, registerModal, onOk };
}, },
}) });
</script> </script>

View File

@ -1,10 +1,10 @@
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes';
export const cronEmits = ['change', 'update:value'] export const cronEmits = ['change', 'update:value'];
export const cronProps = { export const cronProps = {
value: propTypes.string.def(''), value: propTypes.string.def(''),
disabled: propTypes.bool.def(false), disabled: propTypes.bool.def(false),
hideSecond: propTypes.bool.def(false), hideSecond: propTypes.bool.def(false),
hideYear: propTypes.bool.def(false), hideYear: propTypes.bool.def(false),
remote: propTypes.func, remote: propTypes.func,
} };

View File

@ -44,11 +44,11 @@
} }
.tip-info { .tip-info {
color: #999 color: #999;
} }
} }
.allow-click { .allow-click {
cursor: pointer; cursor: pointer;
} }
} }

View File

@ -11,4 +11,4 @@
right: 2px; right: 2px;
} }
} }
} }

View File

@ -1,6 +1,6 @@
// 原开源项目地址https://gitee.com/toktok/easy-cron // 原开源项目地址https://gitee.com/toktok/easy-cron
export { default as JEasyCron } from './EasyCronInput.vue' export { default as JEasyCron } from './EasyCronInput.vue';
export { default as JEasyCronInner } from './EasyCronInner.vue' export { default as JEasyCronInner } from './EasyCronInner.vue';
export { default as JEasyCronModal } from './EasyCronModal.vue' export { default as JEasyCronModal } from './EasyCronModal.vue';
export { default as JCronValidator } from './validator' export { default as JCronValidator } from './validator';

View File

@ -11,23 +11,23 @@
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" />
<span> 日开始间隔 </span> <span> 日开始间隔 </span>
<InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.work" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.work" v-bind="beforeRadioAttrs"></a-radio>
<span> 本月 </span> <span> 本月 </span>
<InputNumber v-model:value="valueWork" v-bind="typeWorkAttrs"/> <InputNumber v-model:value="valueWork" v-bind="typeWorkAttrs" />
<span> 最近的工作日 </span> <span> 最近的工作日 </span>
</div> </div>
<div class="item"> <div class="item">
@ -48,43 +48,46 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, watch } from 'vue' import { computed, defineComponent, watch } from 'vue';
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue';
import { TypeEnum, useTabEmits, useTabProps, useTabSetup } from './useTabMixin' import { TypeEnum, useTabEmits, useTabProps, useTabSetup } from './useTabMixin';
export default defineComponent({ export default defineComponent({
name: 'DayUI', name: 'DayUI',
components: { InputNumber }, components: { InputNumber },
props: useTabProps({ props: useTabProps({
defaultValue: '*',
props: {
week: { type: String, default: '?' },
},
}),
emits: useTabEmits(),
setup(props, context) {
const disabledChoice = computed(() => {
return (props.week && props.week !== '?') || props.disabled
})
const setup = useTabSetup(props, context, {
defaultValue: '*', defaultValue: '*',
valueWork: 1, props: {
minValue: 1, week: { type: String, default: '?' },
maxValue: 31, },
valueRange: { start: 1, end: 31 }, }),
valueLoop: { start: 1, interval: 1 }, emits: useTabEmits(),
disabled: disabledChoice, setup(props, context) {
}) const disabledChoice = computed(() => {
const typeWorkAttrs = computed(() => ({ return (props.week && props.week !== '?') || props.disabled;
disabled: setup.type.value !== TypeEnum.work || props.disabled || disabledChoice.value, });
...setup.inputNumberAttrs.value, const setup = useTabSetup(props, context, {
})) defaultValue: '*',
valueWork: 1,
minValue: 1,
maxValue: 31,
valueRange: { start: 1, end: 31 },
valueLoop: { start: 1, interval: 1 },
disabled: disabledChoice,
});
const typeWorkAttrs = computed(() => ({
disabled: setup.type.value !== TypeEnum.work || props.disabled || disabledChoice.value,
...setup.inputNumberAttrs.value,
}));
watch(() => props.week, () => { watch(
setup.updateValue(disabledChoice.value ? '?' : setup.computeValue.value) () => props.week,
}) () => {
setup.updateValue(disabledChoice.value ? '?' : setup.computeValue.value);
}
);
return { ...setup, typeWorkAttrs } return { ...setup, typeWorkAttrs };
}, },
}) });
</script> </script>

View File

@ -7,17 +7,17 @@
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" />
<span> 时开始间隔 </span> <span> 时开始间隔 </span>
<InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
@ -35,25 +35,25 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue';
import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin' import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin';
export default defineComponent({ export default defineComponent({
name: 'HourUI', name: 'HourUI',
components: { InputNumber }, components: { InputNumber },
props: useTabProps({ props: useTabProps({
defaultValue: '*',
}),
emits: useTabEmits(),
setup(props, context) {
return useTabSetup(props, context, {
defaultValue: '*', defaultValue: '*',
minValue: 0, }),
maxValue: 23, emits: useTabEmits(),
valueRange: { start: 0, end: 23 }, setup(props, context) {
valueLoop: { start: 0, interval: 1 }, return useTabSetup(props, context, {
}) defaultValue: '*',
}, minValue: 0,
}) maxValue: 23,
</script> valueRange: { start: 0, end: 23 },
valueLoop: { start: 0, interval: 1 },
});
},
});
</script>

View File

@ -7,17 +7,17 @@
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" />
<span> 分开始间隔 </span> <span> 分开始间隔 </span>
<InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
@ -35,25 +35,25 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue';
import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin' import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin';
export default defineComponent({ export default defineComponent({
name: 'MinuteUI', name: 'MinuteUI',
components: { InputNumber }, components: { InputNumber },
props: useTabProps({ props: useTabProps({
defaultValue: '*',
}),
emits: useTabEmits(),
setup(props, context) {
return useTabSetup(props, context, {
defaultValue: '*', defaultValue: '*',
minValue: 0, }),
maxValue: 59, emits: useTabEmits(),
valueRange: { start: 0, end: 59 }, setup(props, context) {
valueLoop: { start: 0, interval: 1 }, return useTabSetup(props, context, {
}) defaultValue: '*',
}, minValue: 0,
}) maxValue: 59,
</script> valueRange: { start: 0, end: 59 },
valueLoop: { start: 0, interval: 1 },
});
},
});
</script>

View File

@ -7,17 +7,17 @@
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" />
<span> 月开始间隔 </span> <span> 月开始间隔 </span>
<InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
@ -35,25 +35,25 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue';
import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin' import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin';
export default defineComponent({ export default defineComponent({
name: 'MonthUI', name: 'MonthUI',
components: { InputNumber }, components: { InputNumber },
props: useTabProps({ props: useTabProps({
defaultValue: '*',
}),
emits: useTabEmits(),
setup(props, context) {
return useTabSetup(props, context, {
defaultValue: '*', defaultValue: '*',
minValue: 1, }),
maxValue: 12, emits: useTabEmits(),
valueRange: { start: 1, end: 12 }, setup(props, context) {
valueLoop: { start: 1, interval: 1 }, return useTabSetup(props, context, {
}) defaultValue: '*',
}, minValue: 1,
}) maxValue: 12,
</script> valueRange: { start: 1, end: 12 },
valueLoop: { start: 1, interval: 1 },
});
},
});
</script>

View File

@ -7,17 +7,17 @@
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
<InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs"/> <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" />
<span> 秒开始间隔 </span> <span> 秒开始间隔 </span>
<InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
@ -35,25 +35,25 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue';
import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin' import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin';
export default defineComponent({ export default defineComponent({
name: 'SecondUI', name: 'SecondUI',
components: { InputNumber }, components: { InputNumber },
props: useTabProps({ props: useTabProps({
defaultValue: '*',
}),
emits: useTabEmits(),
setup(props, context) {
return useTabSetup(props, context, {
defaultValue: '*', defaultValue: '*',
minValue: 0, }),
maxValue: 59, emits: useTabEmits(),
valueRange: { start: 0, end: 59 }, setup(props, context) {
valueLoop: { start: 0, interval: 1 }, return useTabSetup(props, context, {
}) defaultValue: '*',
}, minValue: 0,
}) maxValue: 59,
</script> valueRange: { start: 0, end: 59 },
valueLoop: { start: 0, interval: 1 },
});
},
});
</script>

View File

@ -8,16 +8,16 @@
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<a-select v-model:value="valueRange.start" :options="weekOptions" v-bind="typeRangeSelectAttrs"/> <a-select v-model:value="valueRange.start" :options="weekOptions" v-bind="typeRangeSelectAttrs" />
<span> </span> <span> </span>
<a-select v-model:value="valueRange.end" :options="weekOptions" v-bind="typeRangeSelectAttrs"/> <a-select v-model:value="valueRange.end" :options="weekOptions" v-bind="typeRangeSelectAttrs" />
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<a-select v-model:value="valueLoop.start" :options="weekOptions" v-bind="typeLoopSelectAttrs"/> <a-select v-model:value="valueLoop.start" :options="weekOptions" v-bind="typeLoopSelectAttrs" />
<span> 开始间隔 </span> <span> 开始间隔 </span>
<InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs"/> <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
@ -35,88 +35,91 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, watch, defineComponent } from 'vue' import { computed, watch, defineComponent } from 'vue';
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue';
import { useTabProps, useTabEmits, useTabSetup, TypeEnum } from './useTabMixin' import { useTabProps, useTabEmits, useTabSetup, TypeEnum } from './useTabMixin';
const WEEK_MAP_EN = { const WEEK_MAP_EN = {
'1': 'SUN', '1': 'SUN',
'2': 'MON', '2': 'MON',
'3': 'TUE', '3': 'TUE',
'4': 'WED', '4': 'WED',
'5': 'THU', '5': 'THU',
'6': 'FRI', '6': 'FRI',
'7': 'SAT', '7': 'SAT',
} };
const WEEK_MAP_CN = { const WEEK_MAP_CN = {
'1': '周日', '1': '周日',
'2': '周一', '2': '周一',
'3': '周二', '3': '周二',
'4': '周三', '4': '周三',
'5': '周四', '5': '周四',
'6': '周五', '6': '周五',
'7': '周六', '7': '周六',
} };
export default defineComponent({ export default defineComponent({
name: 'WeekUI', name: 'WeekUI',
components: { InputNumber }, components: { InputNumber },
props: useTabProps({ props: useTabProps({
defaultValue: '?',
props: {
day: { type: String, default: '*' },
},
}),
emits: useTabEmits(),
setup(props, context) {
const disabledChoice = computed(() => {
return (props.day && props.day !== '?') || props.disabled
})
const setup = useTabSetup(props, context, {
defaultType: TypeEnum.unset,
defaultValue: '?', defaultValue: '?',
minValue: 1, props: {
maxValue: 7, day: { type: String, default: '*' },
// 0,7 1 },
valueRange: { start: 1, end: 7 }, }),
valueLoop: { start: 2, interval: 1 }, emits: useTabEmits(),
disabled: disabledChoice, setup(props, context) {
}) const disabledChoice = computed(() => {
const weekOptions = computed(() => { return (props.day && props.day !== '?') || props.disabled;
let options: { label: string, value: number }[] = [] });
for (let weekKey of Object.keys(WEEK_MAP_CN)) { const setup = useTabSetup(props, context, {
let weekName: string = WEEK_MAP_CN[weekKey] defaultType: TypeEnum.unset,
options.push({ defaultValue: '?',
value: Number.parseInt(weekKey), minValue: 1,
label: weekName, maxValue: 7,
}) // 0,7 1
} valueRange: { start: 1, end: 7 },
return options valueLoop: { start: 2, interval: 1 },
}) disabled: disabledChoice,
});
const weekOptions = computed(() => {
let options: { label: string; value: number }[] = [];
for (let weekKey of Object.keys(WEEK_MAP_CN)) {
let weekName: string = WEEK_MAP_CN[weekKey];
options.push({
value: Number.parseInt(weekKey),
label: weekName,
});
}
return options;
});
const typeRangeSelectAttrs = computed(() => ({ const typeRangeSelectAttrs = computed(() => ({
class: ['w80'], class: ['w80'],
disabled: setup.typeRangeAttrs.value.disabled, disabled: setup.typeRangeAttrs.value.disabled,
})) }));
const typeLoopSelectAttrs = computed(() => ({ const typeLoopSelectAttrs = computed(() => ({
class: ['w80'], class: ['w80'],
disabled: setup.typeLoopAttrs.value.disabled, disabled: setup.typeLoopAttrs.value.disabled,
})) }));
watch(() => props.day, () => { watch(
setup.updateValue(disabledChoice.value ? '?' : setup.computeValue.value) () => props.day,
}) () => {
setup.updateValue(disabledChoice.value ? '?' : setup.computeValue.value);
}
);
return { return {
...setup, ...setup,
weekOptions, weekOptions,
typeLoopSelectAttrs, typeLoopSelectAttrs,
typeRangeSelectAttrs, typeRangeSelectAttrs,
WEEK_MAP_CN, WEEK_MAP_EN, WEEK_MAP_CN,
} WEEK_MAP_EN,
}, };
},
}) });
</script> </script>

View File

@ -7,17 +7,17 @@
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber class="w80" v-model:value="valueRange.start" v-bind="typeRangeAttrs"/> <InputNumber class="w80" v-model:value="valueRange.start" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
<InputNumber class="w80" v-model:value="valueRange.end" v-bind="typeRangeAttrs"/> <InputNumber class="w80" v-model:value="valueRange.end" v-bind="typeRangeAttrs" />
<span> </span> <span> </span>
</div> </div>
<div class="item"> <div class="item">
<a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio> <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs"></a-radio>
<span> </span> <span> </span>
<InputNumber class="w80" v-model:value="valueLoop.start" v-bind="typeLoopAttrs"/> <InputNumber class="w80" v-model:value="valueLoop.start" v-bind="typeLoopAttrs" />
<span> 年开始间隔 </span> <span> 年开始间隔 </span>
<InputNumber class="w80" v-model:value="valueLoop.interval" v-bind="typeLoopAttrs"/> <InputNumber class="w80" v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" />
<span> </span> <span> </span>
</div> </div>
</a-radio-group> </a-radio-group>
@ -25,25 +25,25 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
import { InputNumber } from 'ant-design-vue' import { InputNumber } from 'ant-design-vue';
import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin' import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin';
export default defineComponent({ export default defineComponent({
name: 'YearUI', name: 'YearUI',
components: { InputNumber }, components: { InputNumber },
props: useTabProps({ props: useTabProps({
defaultValue: '*',
}),
emits: useTabEmits(),
setup(props, context) {
const nowYear = (new Date()).getFullYear()
return useTabSetup(props, context, {
defaultValue: '*', defaultValue: '*',
minValue: 0, }),
valueRange: { start: nowYear, end: nowYear + 100 }, emits: useTabEmits(),
valueLoop: { start: nowYear, interval: 1 }, setup(props, context) {
}) const nowYear = new Date().getFullYear();
}, return useTabSetup(props, context, {
}) defaultValue: '*',
</script> minValue: 0,
valueRange: { start: nowYear, end: nowYear + 100 },
valueLoop: { start: nowYear, interval: 1 },
});
},
});
</script>

View File

@ -1,6 +1,6 @@
// 主要用于日和星期的互斥使用 // 主要用于日和星期的互斥使用
import { computed, inject, reactive, ref, unref, watch } from 'vue' import { computed, inject, reactive, ref, unref, watch } from 'vue';
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes';
export enum TypeEnum { export enum TypeEnum {
unset = 'UNSET', unset = 'UNSET',
@ -14,91 +14,95 @@ export enum TypeEnum {
// use 公共 props // use 公共 props
export function useTabProps(options) { export function useTabProps(options) {
const defaultValue = options?.defaultValue ?? '?' const defaultValue = options?.defaultValue ?? '?';
return { return {
value: propTypes.string.def(defaultValue), value: propTypes.string.def(defaultValue),
disabled: propTypes.bool.def(false), disabled: propTypes.bool.def(false),
...options?.props, ...options?.props,
} };
} }
// use 公共 emits // use 公共 emits
export function useTabEmits() { export function useTabEmits() {
return ['change', 'update:value'] return ['change', 'update:value'];
} }
// use 公共 setup // use 公共 setup
export function useTabSetup(props, context, options) { export function useTabSetup(props, context, options) {
const { emit } = context const { emit } = context;
const prefixCls = inject('prefixCls') const prefixCls = inject('prefixCls');
const defaultValue = ref(options?.defaultValue ?? '?') const defaultValue = ref(options?.defaultValue ?? '?');
// 类型 // 类型
const type = ref(options.defaultType ?? TypeEnum.every) const type = ref(options.defaultType ?? TypeEnum.every);
const valueList = ref<any[]>([]) const valueList = ref<any[]>([]);
// 对于不同的类型,所定义的值也有所不同 // 对于不同的类型,所定义的值也有所不同
const valueRange = reactive(options.valueRange) const valueRange = reactive(options.valueRange);
const valueLoop = reactive(options.valueLoop) const valueLoop = reactive(options.valueLoop);
const valueWeek = reactive(options.valueWeek) const valueWeek = reactive(options.valueWeek);
const valueWork = ref(options.valueWork) const valueWork = ref(options.valueWork);
const maxValue = ref(options.maxValue) const maxValue = ref(options.maxValue);
const minValue = ref(options.minValue) const minValue = ref(options.minValue);
// 根据不同的类型计算出的value // 根据不同的类型计算出的value
const computeValue = computed(() => { const computeValue = computed(() => {
let valueArray: any[] = [] let valueArray: any[] = [];
switch (type.value) { switch (type.value) {
case TypeEnum.unset: case TypeEnum.unset:
valueArray.push('?') valueArray.push('?');
break break;
case TypeEnum.every: case TypeEnum.every:
valueArray.push('*') valueArray.push('*');
break break;
case TypeEnum.range: case TypeEnum.range:
valueArray.push(`${valueRange.start}-${valueRange.end}`) valueArray.push(`${valueRange.start}-${valueRange.end}`);
break break;
case TypeEnum.loop: case TypeEnum.loop:
valueArray.push(`${valueLoop.start}/${valueLoop.interval}`) valueArray.push(`${valueLoop.start}/${valueLoop.interval}`);
break break;
case TypeEnum.work: case TypeEnum.work:
valueArray.push(`${valueWork.value}W`) valueArray.push(`${valueWork.value}W`);
break break;
case TypeEnum.last: case TypeEnum.last:
valueArray.push('L') valueArray.push('L');
break break;
case TypeEnum.specify: case TypeEnum.specify:
if (valueList.value.length === 0) { if (valueList.value.length === 0) {
valueList.value.push(minValue.value) valueList.value.push(minValue.value);
} }
valueArray.push(valueList.value.join(',')) valueArray.push(valueList.value.join(','));
break break;
default: default:
valueArray.push(defaultValue.value) valueArray.push(defaultValue.value);
break break;
} }
return valueArray.length > 0 ? valueArray.join('') : defaultValue.value return valueArray.length > 0 ? valueArray.join('') : defaultValue.value;
}) });
// 指定值范围区间,介于最小值和最大值之间 // 指定值范围区间,介于最小值和最大值之间
const specifyRange = computed(() => { const specifyRange = computed(() => {
let range: number[] = [] let range: number[] = [];
if (maxValue.value != null) { if (maxValue.value != null) {
for (let i = minValue.value; i <= maxValue.value; i++) { for (let i = minValue.value; i <= maxValue.value; i++) {
range.push(i) range.push(i);
} }
} }
return range return range;
}) });
watch(() => props.value, (val) => { watch(
if (val !== computeValue.value) { () => props.value,
parseValue(val) (val) => {
} if (val !== computeValue.value) {
}, { immediate: true }) parseValue(val);
}
},
{ immediate: true }
);
watch(computeValue, (v) => updateValue(v)) watch(computeValue, (v) => updateValue(v));
function updateValue(value) { function updateValue(value) {
emit('change', value) emit('change', value);
emit('update:value', value) emit('update:value', value);
} }
/** /**
@ -107,71 +111,72 @@ export function useTabSetup(props, context, options) {
*/ */
function parseValue(value) { function parseValue(value) {
if (value === computeValue.value) { if (value === computeValue.value) {
return return;
} }
try { try {
if (!value || value === defaultValue.value) { if (!value || value === defaultValue.value) {
type.value = TypeEnum.every type.value = TypeEnum.every;
} else if (value.indexOf('?') >= 0) { } else if (value.indexOf('?') >= 0) {
type.value = TypeEnum.unset type.value = TypeEnum.unset;
} else if (value.indexOf('-') >= 0) { } else if (value.indexOf('-') >= 0) {
type.value = TypeEnum.range type.value = TypeEnum.range;
const values = value.split('-') const values = value.split('-');
if (values.length >= 2) { if (values.length >= 2) {
valueRange.start = parseInt(values[0]) valueRange.start = parseInt(values[0]);
valueRange.end = parseInt(values[1]) valueRange.end = parseInt(values[1]);
} }
} else if (value.indexOf('/') >= 0) { } else if (value.indexOf('/') >= 0) {
type.value = TypeEnum.loop type.value = TypeEnum.loop;
const values = value.split('/') const values = value.split('/');
if (values.length >= 2) { if (values.length >= 2) {
valueLoop.start = value[0] === '*' ? 0 : parseInt(values[0]) valueLoop.start = value[0] === '*' ? 0 : parseInt(values[0]);
valueLoop.interval = parseInt(values[1]) valueLoop.interval = parseInt(values[1]);
} }
} else if (value.indexOf('W') >= 0) { } else if (value.indexOf('W') >= 0) {
type.value = TypeEnum.work type.value = TypeEnum.work;
const values = value.split('W') const values = value.split('W');
if (!values[0] && !isNaN(values[0])) { if (!values[0] && !isNaN(values[0])) {
valueWork.value = parseInt(values[0]) valueWork.value = parseInt(values[0]);
} }
} else if (value.indexOf('L') >= 0) { } else if (value.indexOf('L') >= 0) {
type.value = TypeEnum.last type.value = TypeEnum.last;
} else if (value.indexOf(',') >= 0 || !isNaN(value)) { } else if (value.indexOf(',') >= 0 || !isNaN(value)) {
type.value = TypeEnum.specify type.value = TypeEnum.specify;
valueList.value = value.split(',').map(item => parseInt(item)) valueList.value = value.split(',').map((item) => parseInt(item));
} else { } else {
type.value = TypeEnum.every type.value = TypeEnum.every;
} }
} catch (e) { } catch (e) {
type.value = TypeEnum.every type.value = TypeEnum.every;
} }
} }
const beforeRadioAttrs = computed(() => ({ const beforeRadioAttrs = computed(() => ({
class: ['choice'], class: ['choice'],
disabled: props.disabled || unref(options.disabled), disabled: props.disabled || unref(options.disabled),
})) }));
const inputNumberAttrs = computed(() => ({ const inputNumberAttrs = computed(() => ({
class: ['w60'], class: ['w60'],
max: maxValue.value, max: maxValue.value,
min: minValue.value, min: minValue.value,
precision: 0, precision: 0,
})) }));
const typeRangeAttrs = computed(() => ({ const typeRangeAttrs = computed(() => ({
disabled: type.value !== TypeEnum.range || props.disabled || unref(options.disabled), disabled: type.value !== TypeEnum.range || props.disabled || unref(options.disabled),
...inputNumberAttrs.value, ...inputNumberAttrs.value,
})) }));
const typeLoopAttrs = computed(() => ({ const typeLoopAttrs = computed(() => ({
disabled: type.value !== TypeEnum.loop || props.disabled || unref(options.disabled), disabled: type.value !== TypeEnum.loop || props.disabled || unref(options.disabled),
...inputNumberAttrs.value, ...inputNumberAttrs.value,
})) }));
const typeSpecifyAttrs = computed(() => ({ const typeSpecifyAttrs = computed(() => ({
disabled: type.value !== TypeEnum.specify || props.disabled || unref(options.disabled), disabled: type.value !== TypeEnum.specify || props.disabled || unref(options.disabled),
class: ['list-check-item'], class: ['list-check-item'],
})) }));
return { return {
type, TypeEnum, type,
TypeEnum,
prefixCls, prefixCls,
defaultValue, defaultValue,
valueRange, valueRange,
@ -190,5 +195,5 @@ export function useTabSetup(props, context, options) {
typeRangeAttrs, typeRangeAttrs,
typeLoopAttrs, typeLoopAttrs,
typeSpecifyAttrs, typeSpecifyAttrs,
} };
} }

View File

@ -1,48 +1,48 @@
import CronParser from 'cron-parser' import CronParser from 'cron-parser';
import type { ValidatorRule } from 'ant-design-vue/lib/form/interface' import type { ValidatorRule } from 'ant-design-vue/lib/form/interface';
const cronRule: ValidatorRule = { const cronRule: ValidatorRule = {
validator({}, value) { validator({}, value) {
// 没填写就不校验 // 没填写就不校验
if (!value) { if (!value) {
return Promise.resolve() return Promise.resolve();
} }
const values: string[] = value.split(' ').filter(item => !!item) const values: string[] = value.split(' ').filter((item) => !!item);
if (values.length > 7) { if (values.length > 7) {
return Promise.reject('Cron表达式最多7项') return Promise.reject('Cron表达式最多7项');
} }
// 检查第7项 // 检查第7项
let val: string = value let val: string = value;
if (values.length === 7) { if (values.length === 7) {
const year = values[6] const year = values[6];
if (year !== '*' && year !== '?') { if (year !== '*' && year !== '?') {
let yearValues: string[] = [] let yearValues: string[] = [];
if (year.indexOf('-') >= 0) { if (year.indexOf('-') >= 0) {
yearValues = year.split('-') yearValues = year.split('-');
} else if (year.indexOf('/')) { } else if (year.indexOf('/')) {
yearValues = year.split('/') yearValues = year.split('/');
} else { } else {
yearValues = [year] yearValues = [year];
} }
// 判断是否都是数字 // 判断是否都是数字
const checkYear = yearValues.some(item => isNaN(Number(item))) const checkYear = yearValues.some((item) => isNaN(Number(item)));
if (checkYear) { if (checkYear) {
return Promise.reject('Cron表达式参数[年]错误:' + year) return Promise.reject('Cron表达式参数[年]错误:' + year);
} }
} }
// 取其中的前六项 // 取其中的前六项
val = values.slice(0, 6).join(' ') val = values.slice(0, 6).join(' ');
} }
// 6位 没有年 // 6位 没有年
// 5位没有秒、年 // 5位没有秒、年
try { try {
const iter = CronParser.parseExpression(val) const iter = CronParser.parseExpression(val);
iter.next() iter.next();
return Promise.resolve() return Promise.resolve();
} catch (e) { } catch (e) {
return Promise.reject('Cron表达式错误' + e) return Promise.reject('Cron表达式错误' + e);
} }
}, },
} };
export default cronRule.validator export default cronRule.validator;

View File

@ -1,40 +1,39 @@
<template> <template>
<Tinymce v-bind="bindProps" @change="onChange"/> <Tinymce v-bind="bindProps" @change="onChange" />
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue' import { computed, defineComponent } from 'vue';
import { Tinymce } from '/@/components/Tinymce' import { Tinymce } from '/@/components/Tinymce';
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes';
export default defineComponent({ export default defineComponent({
name: 'JEditor', name: 'JEditor',
// attrs html // attrs html
inheritAttrs: false, inheritAttrs: false,
components: { Tinymce }, components: { Tinymce },
props: { props: {
value: propTypes.string.def(''), value: propTypes.string.def(''),
disabled: propTypes.bool.def(false), disabled: propTypes.bool.def(false),
}, },
emits: ['change', 'update:value'], emits: ['change', 'update:value'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
// props attrs // props attrs
const bindProps = computed(() => Object.assign({}, props, attrs)) const bindProps = computed(() => Object.assign({}, props, attrs));
// value change // value change
function onChange(value) { function onChange(value) {
emit('change', value) emit('change', value);
emit('update:value', value) emit('update:value', value);
} }
return { return {
bindProps, bindProps,
onChange, onChange,
} };
}, },
}) });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped></style>
</style>

View File

@ -1,20 +1,19 @@
<template> <template>
<a-tooltip placement="topLeft"> <a-tooltip placement="topLeft">
<template #title> <template #title>
<span>{{value}}</span> <span>{{ value }}</span>
</template> </template>
{{ showText }} {{ showText }}
</a-tooltip> </a-tooltip>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {computed} from 'vue'; import { computed } from 'vue';
import {propTypes} from "/@/utils/propTypes"; import { propTypes } from '/@/utils/propTypes';
const props = defineProps({
value: propTypes.string.def(''),
length: propTypes.number.def(25),
});
//
const showText = computed(() => props.value ? (props.value.length > props.length ? props.value.slice(0, props.length) + '...' : props.value) : props.value);
const props = defineProps({
value: propTypes.string.def(''),
length: propTypes.number.def(25),
});
//
const showText = computed(() => (props.value ? (props.value.length > props.length ? props.value.slice(0, props.length) + '...' : props.value) : props.value));
</script> </script>

View File

@ -1,32 +1,33 @@
<template> <template>
<div class="clearfix"> <div class="clearfix">
<a-upload <a-upload
:listType="listType" :listType="listType"
:multiple="multiple" :multiple="multiple"
:action="uploadUrl" :action="uploadUrl"
:headers="headers" :headers="headers"
:data="{biz:bizPath}" :data="{ biz: bizPath }"
v-model:fileList="uploadFileList" v-model:fileList="uploadFileList"
:beforeUpload="beforeUpload" :beforeUpload="beforeUpload"
:disabled="disabled" :disabled="disabled"
@change="handleChange" @change="handleChange"
@preview="handlePreview"> @preview="handlePreview"
<div v-if="uploadVisible"> >
<div v-if="listType=='picture-card'"> <div v-if="uploadVisible">
<LoadingOutlined v-if="loading"/> <div v-if="listType == 'picture-card'">
<UploadOutlined v-else/> <LoadingOutlined v-if="loading" />
<div class="ant-upload-text">{{ text }}</div> <UploadOutlined v-else />
</div> <div class="ant-upload-text">{{ text }}</div>
<a-button v-if="listType=='picture'"> </div>
<UploadOutlined></UploadOutlined> <a-button v-if="listType == 'picture'">
{{ text }} <UploadOutlined></UploadOutlined>
</a-button> {{ text }}
</div> </a-button>
</a-upload> </div>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel()"> </a-upload>
<img alt="example" style="width: 100%" :src="previewImage"/> <a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel()">
</a-modal> <img alt="example" style="width: 100%" :src="previewImage" />
</div> </a-modal>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted } from 'vue'; import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted } from 'vue';
@ -46,10 +47,7 @@
inheritAttrs: false, inheritAttrs: false,
props: { props: {
// //
value: propTypes.oneOfType([ value: propTypes.oneOfType([propTypes.string, propTypes.array]),
propTypes.string,
propTypes.array,
]),
// //
listType: { listType: {
type: String, type: String,
@ -81,7 +79,7 @@
default: 1, default: 1,
}, },
}, },
emits: ['options-change', 'change','update:value'], emits: ['options-change', 'change', 'update:value'],
setup(props, { emit, refs }) { setup(props, { emit, refs }) {
const emitData = ref<any[]>([]); const emitData = ref<any[]>([]);
const attrs = useAttrs(); const attrs = useAttrs();
@ -114,7 +112,6 @@
return props['fileMax'] > 1; return props['fileMax'] > 1;
}); });
// //
const uploadVisible = computed(() => { const uploadVisible = computed(() => {
return uploadFileList.value.length < props['fileMax']; return uploadFileList.value.length < props['fileMax'];
@ -132,7 +129,7 @@
if (initTag.value == true) { if (initTag.value == true) {
initFileList(val); initFileList(val);
} }
}, }
); );
/** /**
@ -194,7 +191,7 @@
} }
emitData.value = fileUrls.join(','); emitData.value = fileUrls.join(',');
state.value = fileUrls.join(','); state.value = fileUrls.join(',');
emit('update:value',fileUrls.join(',')) emit('update:value', fileUrls.join(','));
} }
/** /**
@ -244,13 +241,13 @@
}); });
</script> </script>
<style scoped> <style scoped>
.ant-upload-select-picture-card i { .ant-upload-select-picture-card i {
font-size: 32px; font-size: 32px;
color: #999; color: #999;
} }
.ant-upload-select-picture-card .ant-upload-text { .ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px; margin-top: 8px;
color: #666; color: #666;
} }
</style> </style>

View File

@ -1,179 +1,179 @@
<template> <template>
<div> <div>
<BasicModal v-bind="$attrs" @register="register" title="导入EXCEL" :width="600" @cancel="handleClose" :confirmLoading="uploading" destroyOnClose> <BasicModal v-bind="$attrs" @register="register" title="导入EXCEL" :width="600" @cancel="handleClose" :confirmLoading="uploading" destroyOnClose>
<!--是否校验--> <!--是否校验-->
<div style="margin: 0 5px 1px" v-if="online"> <div style="margin: 0 5px 1px" v-if="online">
<span style="display: inline-block;height: 32px;line-height: 32px;vertical-align: middle;">是否开启校验:</span> <span style="display: inline-block; height: 32px; line-height: 32px; vertical-align: middle">是否开启校验:</span>
<span style="margin-left: 6px"> <span style="margin-left: 6px">
<a-switch :checked="validateStatus==1" @change="handleChangeValidateStatus" checked-children="" un-checked-children="" size="small"/> <a-switch :checked="validateStatus == 1" @change="handleChangeValidateStatus" checked-children="" un-checked-children="" size="small" />
</span> </span>
</div> </div>
<!--上传--> <!--上传-->
<a-upload name="file" accept=".xls,.xlsx" :multiple="true" :fileList="fileList" :remove="handleRemove" :beforeUpload="beforeUpload"> <a-upload name="file" accept=".xls,.xlsx" :multiple="true" :fileList="fileList" :remove="handleRemove" :beforeUpload="beforeUpload">
<a-button preIcon="ant-design:upload-outlined">选择导入文件</a-button> <a-button preIcon="ant-design:upload-outlined">选择导入文件</a-button>
</a-upload> </a-upload>
<!--页脚--> <!--页脚-->
<template #footer> <template #footer>
<a-button @click="handleClose"></a-button> <a-button @click="handleClose"></a-button>
<a-button type="primary" @click="handleImport" :disabled="uploadDisabled" :loading="uploading">{{ uploading ? '上传中...' : '开始上传' }}</a-button> <a-button type="primary" @click="handleImport" :disabled="uploadDisabled" :loading="uploading">{{ uploading ? '上传中...' : '开始上传' }}</a-button>
</template> </template>
</BasicModal> </BasicModal>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, ref, unref, watchEffect, computed} from 'vue'; import { defineComponent, ref, unref, watchEffect, computed } from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import {useAttrs} from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import {defHttp} from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import {useGlobSetting} from '/@/hooks/setting'; import { useGlobSetting } from '/@/hooks/setting';
import {useMessage} from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({ export default defineComponent({
name: 'JImportModal', name: 'JImportModal',
components: { components: {
BasicModal, BasicModal,
}, },
props: { props: {
url: { url: {
type: String, type: String,
default: '', default: '',
required: false required: false,
}, },
biz: { biz: {
type: String, type: String,
default: '', default: '',
required: false required: false,
}, },
//online //online
online: { online: {
type: Boolean, type: Boolean,
default: false, default: false,
required: false required: false,
},
},
emits: ['ok', 'register'],
setup(props, { emit, refs }) {
const { createMessage, createWarningModal } = useMessage();
//
const [register, { closeModal }] = useModalInner((data) => {
reset(data);
});
const glob = useGlobSetting();
const attrs = useAttrs();
const uploading = ref(false);
//
const fileList = ref([]);
//url
const uploadAction = ref('');
const foreignKeys = ref('');
//
const validateStatus = ref(0);
const getBindValue = Object.assign({}, unref(props), unref(attrs));
//url
watchEffect(() => {
props.url && (uploadAction.value = `${glob.uploadUrl}${props.url}`);
});
//disabled
const uploadDisabled = computed(() => !(unref(fileList).length > 0));
//
function handleClose() {
closeModal() && reset();
}
//
function handleChangeValidateStatus(checked) {
validateStatus.value = !!checked ? 1 : 0;
}
//
function handleRemove(file) {
const index = unref(fileList).indexOf(file);
const newFileList = unref(fileList).slice();
newFileList.splice(index, 1);
fileList.value = newFileList;
}
//
function beforeUpload(file) {
fileList.value = [...unref(fileList), file];
return false;
}
//
function handleImport() {
let { biz, online } = props;
const formData = new FormData();
if (biz) {
formData.append('isSingleTableImport', biz);
}
if (unref(foreignKeys) && unref(foreignKeys).length > 0) {
formData.append('foreignKeys', unref(foreignKeys));
}
if (!!online) {
formData.append('validateStatus', unref(validateStatus));
}
unref(fileList).forEach((file) => {
formData.append('files[]', file);
});
uploading.value = true;
//TODO
let headers = {
'Content-Type': 'multipart/form-data;boundary = ' + new Date().getTime(),
};
defHttp.post({ url: props.url, params: formData, headers }, { isTransformResponse: false }).then((res) => {
uploading.value = false;
if (res.success) {
if (res.code == 201) {
errorTip(res.message, res.result);
} else {
createMessage.success(res.message);
} }
}, handleClose();
emits: ['ok','register'], reset();
setup(props, {emit, refs}) { emit('ok');
const {createMessage, createWarningModal} = useMessage(); } else {
// createMessage.warning(res.message);
const [register, {closeModal}] = useModalInner((data) => { }
reset(data); });
}); }
const glob = useGlobSetting();
const attrs = useAttrs();
const uploading = ref(false);
//
const fileList = ref([]);
//url
const uploadAction = ref('');
const foreignKeys = ref('');
//
const validateStatus = ref(0);
const getBindValue = Object.assign({}, unref(props), unref(attrs));
//url
watchEffect(() => {
props.url && (uploadAction.value = `${glob.uploadUrl}${props.url}`);
});
//disabled
const uploadDisabled = computed(() => !(unref(fileList).length > 0));
// //
function handleClose() { function errorTip(tipMessage, fileUrl) {
closeModal() && reset() let href = glob.uploadUrl + fileUrl;
} createWarningModal({
title: '导入成功,但是有错误数据!',
// centered: false,
function handleChangeValidateStatus(checked) { content: `<div>
validateStatus.value = !!checked ? 1 : 0;
}
//
function handleRemove(file) {
const index = unref(fileList).indexOf(file);
const newFileList = unref(fileList).slice();
newFileList.splice(index, 1);
fileList.value = newFileList
}
//
function beforeUpload(file) {
fileList.value = [...unref(fileList), file];
return false;
}
//
function handleImport() {
let {biz, online} = props;
const formData = new FormData();
if (biz) {
formData.append('isSingleTableImport', biz);
}
if (unref(foreignKeys) && unref(foreignKeys).length > 0) {
formData.append('foreignKeys', unref(foreignKeys));
}
if (!!online) {
formData.append('validateStatus', unref(validateStatus));
}
unref(fileList).forEach((file) => {
formData.append('files[]', file);
});
uploading.value = true;
//TODO
let headers={
'Content-Type': 'multipart/form-data;boundary = ' + new Date().getTime()
}
defHttp.post({url: props.url,params:formData,headers},{isTransformResponse:false}).then((res) => {
uploading.value = false;
if (res.success) {
if (res.code == 201) {
errorTip(res.message, res.result)
} else {
createMessage.success(res.message)
}
handleClose();
reset();
emit('ok')
} else {
createMessage.warning(res.message)
}
})
}
//
function errorTip(tipMessage, fileUrl) {
let href = glob.uploadUrl + fileUrl;
createWarningModal({
title: '导入成功,但是有错误数据!',
centered: false,
content: `<div>
<span>${tipMessage}</span><br/> <span>${tipMessage}</span><br/>
<span>具体详情请<a href = ${href} target="_blank"> 点击下载 </a> </span> <span>具体详情请<a href = ${href} target="_blank"> 点击下载 </a> </span>
</div>` </div>`,
}) });
} }
// //
function reset(arg?) { function reset(arg?) {
fileList.value = []; fileList.value = [];
uploading.value = false; uploading.value = false;
foreignKeys.value = arg; foreignKeys.value = arg;
validateStatus.value = 0 validateStatus.value = 0;
} }
return { return {
register, register,
getBindValue, getBindValue,
uploadDisabled, uploadDisabled,
fileList, fileList,
uploading, uploading,
validateStatus, validateStatus,
handleClose, handleClose,
handleChangeValidateStatus, handleChangeValidateStatus,
handleRemove, handleRemove,
beforeUpload, beforeUpload,
handleImport, handleImport,
}; };
}, },
}); });
</script> </script>

View File

@ -1,107 +1,105 @@
<template> <template>
<a-input v-bind="getBindValue" v-model:value="showText" @input="backValue"></a-input> <a-input v-bind="getBindValue" v-model:value="showText" @input="backValue"></a-input>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, PropType, ref, watchEffect, unref, watch} from 'vue'; import { defineComponent, PropType, ref, watchEffect, unref, watch } from 'vue';
import {useAttrs} from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import {propTypes} from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import {JInputTypeEnum} from '/@/enums/jeecgEnum.ts'; import { JInputTypeEnum } from '/@/enums/jeecgEnum.ts';
export default defineComponent({ export default defineComponent({
name: 'JInput', name: 'JInput',
inheritAttrs: false, inheritAttrs: false,
props: { props: {
value: propTypes.string.def(''), value: propTypes.string.def(''),
type: propTypes.string.def(JInputTypeEnum.JINPUT_QUERY_LIKE), type: propTypes.string.def(JInputTypeEnum.JINPUT_QUERY_LIKE),
placeholder: propTypes.string.def(''), placeholder: propTypes.string.def(''),
trim: propTypes.bool.def(false), trim: propTypes.bool.def(false),
},
emits: ['change', 'update:value'],
setup(props, { emit }) {
const attrs = useAttrs();
//
const showText = ref('');
//
const getBindValue = Object.assign({}, unref(props), unref(attrs));
//
watch(
() => props.type,
(val) => {
val && backValue({ target: { value: unref(showText) } });
}
);
//value
watch(
() => props.value,
() => {
initVal();
}, },
emits: ['change','update:value'], { immediate: true }
setup(props, {emit}) { );
const attrs = useAttrs();
//
const showText = ref('');
//
const getBindValue = Object.assign({}, unref(props), unref(attrs));
//
watch(
() => props.type,
(val) => {
val && backValue({target: {value: unref(showText)}});
}
);
//value
watch(
() => props.value,
() => {
initVal();
},
{immediate: true}
);
/** /**
* 初始化数值 * 初始化数值
*/ */
function initVal() { function initVal() {
if (!props.value) { if (!props.value) {
showText.value = '' showText.value = '';
} else { } else {
let text = props.value; let text = props.value;
switch (props.type) { switch (props.type) {
case JInputTypeEnum.JINPUT_QUERY_LIKE: case JInputTypeEnum.JINPUT_QUERY_LIKE:
//jinput #1336 //jinput #1336
if (text.indexOf("*") != -1) { if (text.indexOf('*') != -1) {
text = text.substring(1, text.length - 1); text = text.substring(1, text.length - 1);
} }
break; break;
case JInputTypeEnum.JINPUT_QUERY_NE: case JInputTypeEnum.JINPUT_QUERY_NE:
text = text.substring(1); text = text.substring(1);
break; break;
case JInputTypeEnum.JINPUT_QUERY_GE: case JInputTypeEnum.JINPUT_QUERY_GE:
text = text.substring(2); text = text.substring(2);
break; break;
case JInputTypeEnum.JINPUT_QUERY_LE: case JInputTypeEnum.JINPUT_QUERY_LE:
text = text.substring(2); text = text.substring(2);
break; break;
default: default:
} }
showText.value = text showText.value = text;
} }
} }
/** /**
* 返回值 * 返回值
*/ */
function backValue(e) { function backValue(e) {
let text = e?.target?.value??''; let text = e?.target?.value ?? '';
if (text && !!props.trim) { if (text && !!props.trim) {
text = text.trim() text = text.trim();
} }
switch (props.type) { switch (props.type) {
case JInputTypeEnum.JINPUT_QUERY_LIKE: case JInputTypeEnum.JINPUT_QUERY_LIKE:
text = "*" + text + "*"; text = '*' + text + '*';
break; break;
case JInputTypeEnum.JINPUT_QUERY_NE: case JInputTypeEnum.JINPUT_QUERY_NE:
text = "!" + text; text = '!' + text;
break; break;
case JInputTypeEnum.JINPUT_QUERY_GE: case JInputTypeEnum.JINPUT_QUERY_GE:
text = ">=" + text; text = '>=' + text;
break; break;
case JInputTypeEnum.JINPUT_QUERY_LE: case JInputTypeEnum.JINPUT_QUERY_LE:
text = "<=" + text; text = '<=' + text;
break; break;
default: default:
} }
emit("change", text) emit('change', text);
emit("update:value", text) emit('update:value', text);
} }
return {showText, attrs, getBindValue, backValue}; return { showText, attrs, getBindValue, backValue };
}, },
}); });
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -1,122 +1,109 @@
<template> <template>
<a-popover <a-popover trigger="contextmenu" v-model:visible="visible" :overlayClassName="`${prefixCls}-popover`" :getPopupContainer="getPopupContainer" :placement="position">
trigger="contextmenu"
v-model:visible="visible"
:overlayClassName="`${prefixCls}-popover`"
:getPopupContainer="getPopupContainer"
:placement="position">
<template #title> <template #title>
<span>{{ title }}</span> <span>{{ title }}</span>
<span style="float: right" title="关闭"> <span style="float: right" title="关闭">
<Icon icon="ant-design:close-outlined" @click="visible = false"/> <Icon icon="ant-design:close-outlined" @click="visible = false" />
</span> </span>
</template> </template>
<template #content> <template #content>
<a-textarea <a-textarea ref="textareaRef" :value="innerValue" :disabled="disabled" :style="textareaStyle" v-bind="attrs" @input="onInputChange" />
ref="textareaRef"
:value="innerValue"
:disabled="disabled"
:style="textareaStyle"
v-bind="attrs"
@input="onInputChange"/>
</template> </template>
<a-input <a-input :class="`${prefixCls}-input`" :value="innerValue" :disabled="disabled" v-bind="attrs" @change="onInputChange">
:class="`${prefixCls}-input`"
:value="innerValue"
:disabled="disabled"
v-bind="attrs"
@change="onInputChange">
<template #suffix> <template #suffix>
<Icon icon="ant-design:fullscreen-outlined" @click.stop="onShowPopup"/> <Icon icon="ant-design:fullscreen-outlined" @click.stop="onShowPopup" />
</template> </template>
</a-input> </a-input>
</a-popover> </a-popover>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, ref, watch } from 'vue' import { computed, nextTick, ref, watch } from 'vue';
import Icon from '/@/components/Icon/src/Icon.vue' import Icon from '/@/components/Icon/src/Icon.vue';
import { useAttrs } from '/@/hooks/core/useAttrs' import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes';
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign';
const { prefixCls } = useDesign('j-input-popup') const { prefixCls } = useDesign('j-input-popup');
const props = defineProps({ const props = defineProps({
// v-model:value // v-model:value
value: propTypes.string.def(''), value: propTypes.string.def(''),
title: propTypes.string.def(''), title: propTypes.string.def(''),
// //
position: propTypes.string.def('right'), position: propTypes.string.def('right'),
width: propTypes.number.def(300), width: propTypes.number.def(300),
height: propTypes.number.def(150), height: propTypes.number.def(150),
disabled: propTypes.bool.def(false), disabled: propTypes.bool.def(false),
// ID // ID
popContainer: propTypes.string.def(''), popContainer: propTypes.string.def(''),
}) });
const attrs = useAttrs() const attrs = useAttrs();
const emit = defineEmits(['change', 'update:value']) const emit = defineEmits(['change', 'update:value']);
const visible = ref<boolean>(false) const visible = ref<boolean>(false);
const innerValue = ref<string>('') const innerValue = ref<string>('');
// textarea ref // textarea ref
const textareaRef = ref() const textareaRef = ref();
// textarea // textarea
const textareaStyle = computed(() => ({ const textareaStyle = computed(() => ({
height: `${props.height}px`, height: `${props.height}px`,
width: `${props.width}px`, width: `${props.width}px`,
})) }));
watch(() => props.value, (value) => { watch(
if (value && value.length > 0) { () => props.value,
innerValue.value = value (value) => {
if (value && value.length > 0) {
innerValue.value = value;
}
},
{ immediate: true }
);
function onInputChange(event) {
innerValue.value = event.target.value;
emitValue(innerValue.value);
} }
}, { immediate: true })
async function onShowPopup() {
function onInputChange(event) { visible.value = true;
innerValue.value = event.target.value await nextTick();
emitValue(innerValue.value) textareaRef.value?.focus();
}
async function onShowPopup() {
visible.value = true
await nextTick()
textareaRef.value?.focus()
}
//
function getPopupContainer(node) {
if (!props.popContainer) {
return node.parentNode
} else {
return document.getElementById(props.popContainer)
} }
}
function emitValue(value) { //
emit('change', value) function getPopupContainer(node) {
emit('update:value', value) if (!props.popContainer) {
} return node.parentNode;
} else {
return document.getElementById(props.popContainer);
}
}
function emitValue(value) {
emit('change', value);
emit('update:value', value);
}
</script> </script>
<style lang="less"> <style lang="less">
//noinspection LessUnresolvedVariable //noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-j-input-popup'; @prefix-cls: ~'@{namespace}-j-input-popup';
.@{prefix-cls} { .@{prefix-cls} {
&-popover { &-popover {
} }
&-input { &-input {
.app-iconify { .app-iconify {
cursor: pointer; cursor: pointer;
color: #666666; color: #666666;
transition: color 0.3s; transition: color 0.3s;
&:hover { &:hover {
color: black; color: black;
}
} }
} }
} }
} </style>
</style>

View File

@ -1,54 +1,57 @@
<template> <template>
<MarkDown v-bind="bindProps" @change="onChange" @get="onGetVditor"/> <MarkDown v-bind="bindProps" @change="onChange" @get="onGetVditor" />
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, watch } from 'vue' import { computed, defineComponent, watch } from 'vue';
import { MarkDown } from '/@/components/Markdown' import { MarkDown } from '/@/components/Markdown';
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes';
export default defineComponent({ export default defineComponent({
name: 'JMarkdownEditor', name: 'JMarkdownEditor',
// attrs html // attrs html
inheritAttrs: false, inheritAttrs: false,
components: { MarkDown }, components: { MarkDown },
props: { props: {
value: propTypes.string.def(''), value: propTypes.string.def(''),
disabled: propTypes.bool.def(false), disabled: propTypes.bool.def(false),
}, },
emits: ['change', 'update:value'], emits: ['change', 'update:value'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
// markdown // markdown
let mdRef: any = null let mdRef: any = null;
// vditor // vditor
let vditorRef: any = null let vditorRef: any = null;
// props attrs // props attrs
const bindProps = computed(() => Object.assign({}, props, attrs)) const bindProps = computed(() => Object.assign({}, props, attrs));
// onMounted // onMounted
function onGetVditor(instance) { function onGetVditor(instance) {
mdRef = instance mdRef = instance;
vditorRef = mdRef.getVditor() vditorRef = mdRef.getVditor();
// //
watch(() => props.disabled, disabled => disabled ? vditorRef.disabled() : vditorRef.enable(), { immediate: true }) watch(
} () => props.disabled,
(disabled) => (disabled ? vditorRef.disabled() : vditorRef.enable()),
{ immediate: true }
);
}
// value change // value change
function onChange(value) { function onChange(value) {
emit('change', value) emit('change', value);
emit('update:value', value) emit('update:value', value);
} }
return { return {
bindProps, bindProps,
onChange, onChange,
onGetVditor, onGetVditor,
} };
}, },
}) });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped></style>
</style>

View File

@ -1,24 +1,24 @@
<template> <template>
<!-- 级联下拉框 form组件 暂且只在online使用 不对外提供api --> <!-- 级联下拉框 form组件 暂且只在online使用 不对外提供api -->
<a-select :placeholder="placeholder" :value="selectedValue" @change="handleChange" allowClear style="width:100%"> <a-select :placeholder="placeholder" :value="selectedValue" @change="handleChange" allowClear style="width: 100%">
<a-select-option v-for="(item, index) in dictOptions" :key="index" :value="item.store"> <a-select-option v-for="(item, index) in dictOptions" :key="index" :value="item.store">
<span style="display: inline-block;width: 100%" :title=" item.label ">{{ item.label }}</span> <span style="display: inline-block; width: 100%" :title="item.label">{{ item.label }}</span>
</a-select-option> </a-select-option>
</a-select> </a-select>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, watch, ref } from 'vue'; import { defineComponent, watch, ref } from 'vue';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
/**获取下拉选项*/ /**获取下拉选项*/
const SELECT_OPTIONS_URL = '/online/cgform/api/querySelectOptions'; const SELECT_OPTIONS_URL = '/online/cgform/api/querySelectOptions';
export default defineComponent({ export default defineComponent({
name: "JOnlineSelectCascade", name: 'JOnlineSelectCascade',
props:{ props: {
table:{ type: String, default: '' }, table: { type: String, default: '' },
txt: { type: String, default: '' }, txt: { type: String, default: '' },
store: { type: String, default: '' }, store: { type: String, default: '' },
idField: { type: String, default: '' }, idField: { type: String, default: '' },
@ -26,188 +26,195 @@
pidValue: { type: String, default: '-1' }, pidValue: { type: String, default: '-1' },
origin: { type: Boolean, default: false }, origin: { type: Boolean, default: false },
condition: { type: String, default: '' }, condition: { type: String, default: '' },
value:{ type: String, default: '' }, value: { type: String, default: '' },
isNumber:{ type: Boolean, default: false }, isNumber: { type: Boolean, default: false },
placeholder: { type: String, default: '请选择' }, placeholder: { type: String, default: '请选择' },
}, },
emits: [ 'change', 'next'], emits: ['change', 'next'],
setup(props, { emit }){ setup(props, { emit }) {
const { createMessage: $message } = useMessage() const { createMessage: $message } = useMessage();
// //
const selectedValue = ref<any>(''); const selectedValue = ref<any>('');
// //
const dictOptions = ref<any[]>([]) const dictOptions = ref<any[]>([]);
const optionsLoad = ref(true) const optionsLoad = ref(true);
// //
function handleChange(value) { function handleChange(value) {
console.log('handleChange', value) console.log('handleChange', value);
// value id // value id
let temp = value || '' let temp = value || '';
emit('change', temp) emit('change', temp);
valueChangeThenEmitNext(temp) valueChangeThenEmitNext(temp);
} }
// condition // condition
watch(()=>props.condition, (val)=>{ watch(
optionsLoad.value = true; () => props.condition,
if(val){ (val) => {
loadOptions(); optionsLoad.value = true;
} if (val) {
}, {immediate:true}); loadOptions();
}
},
{ immediate: true }
);
// pidValue // pidValue
watch(()=>props.pidValue, (val)=>{ watch(
if(val === '-1'){ () => props.pidValue,
dictOptions.value = [] (val) => {
}else{ if (val === '-1') {
loadOptions(); dictOptions.value = [];
} else {
loadOptions();
}
} }
}); );
// //
watch(()=>props.value, (newVal, oldVal)=>{ watch(
console.log('值改变事件', newVal, oldVal) () => props.value,
if(!newVal){ (newVal, oldVal) => {
// value-- console.log('值改变事件', newVal, oldVal);
selectedValue.value = [] if (!newVal) {
if(oldVal){ // value--
// oldVal selectedValue.value = [];
emit('change', '') if (oldVal) {
emit('next', '-1') // oldVal
emit('change', '');
emit('next', '-1');
}
} else {
// value
selectedValue.value = newVal;
} }
}else{ if (newVal && !oldVal) {
// value // options
selectedValue.value = newVal handleFirstValueSetting(newVal);
} }
if(newVal && !oldVal){ },
// options { immediate: true }
handleFirstValueSetting(newVal) );
}
}, {immediate:true});
/** /**
* 第一次加载赋值 * 第一次加载赋值
*/ */
async function handleFirstValueSetting(value){ async function handleFirstValueSetting(value) {
if(props.idField === props.store){ if (props.idField === props.store) {
// id // id
emit('next', value) emit('next', value);
}else{ } else {
if(props.origin===true){ if (props.origin === true) {
// optionsoptions // optionsoptions
await getSelfOptions() await getSelfOptions();
valueChangeThenEmitNext(value) valueChangeThenEmitNext(value);
}else{ } else {
// value // value
let arr = await loadValueText(); let arr = await loadValueText();
valueChangeThenEmitNext(value, arr) valueChangeThenEmitNext(value, arr);
} }
} }
} }
function loadOptions() { function loadOptions() {
let params = getQueryParams(); let params = getQueryParams();
if(props.origin===true){ if (props.origin === true) {
params['condition'] = props.condition params['condition'] = props.condition;
}else{ } else {
params['pidValue'] = props.pidValue params['pidValue'] = props.pidValue;
} }
console.log("请求参数", params) console.log('请求参数', params);
dictOptions.value = [] dictOptions.value = [];
defHttp.get({ url: SELECT_OPTIONS_URL, params},{ isTransformResponse: false }).then(res=>{ defHttp.get({ url: SELECT_OPTIONS_URL, params }, { isTransformResponse: false }).then((res) => {
if(res.success){ if (res.success) {
dictOptions.value = [...res.result] dictOptions.value = [...res.result];
console.log("请求结果", res.result, dictOptions) console.log('请求结果', res.result, dictOptions);
}else{ } else {
$message.warning('联动组件数据加载失败,请检查配置!') $message.warning('联动组件数据加载失败,请检查配置!');
} }
}) });
} }
function getQueryParams(){ function getQueryParams() {
let params = { let params = {
table: props.table, table: props.table,
txt: props.txt, txt: props.txt,
key: props.store, key: props.store,
idField: props.idField, idField: props.idField,
pidField: props.pidField pidField: props.pidField,
} };
return params; return params;
} }
function loadValueText() { function loadValueText() {
return new Promise(resolve => { return new Promise((resolve) => {
if(!props.value){ if (!props.value) {
selectedValue.value = [] selectedValue.value = [];
resolve([]) resolve([]);
}else{ } else {
let params = getQueryParams(); let params = getQueryParams();
if(props.isNumber === true){ if (props.isNumber === true) {
params['condition'] = `${props.store} = ${props.value}` params['condition'] = `${props.store} = ${props.value}`;
}else{ } else {
params['condition'] = `${props.store} = '${props.value}'` params['condition'] = `${props.store} = '${props.value}'`;
} }
defHttp.get({ url: SELECT_OPTIONS_URL, params},{ isTransformResponse: false }).then(res=>{ defHttp.get({ url: SELECT_OPTIONS_URL, params }, { isTransformResponse: false }).then((res) => {
if(res.success){ if (res.success) {
resolve(res.result) resolve(res.result);
}else{ } else {
$message.warning('联动组件数据加载失败,请检查配置!') $message.warning('联动组件数据加载失败,请检查配置!');
resolve([]) resolve([]);
} }
}) });
} }
}) });
} }
/** /**
* 获取下拉选项 * 获取下拉选项
*/ */
function getSelfOptions() { function getSelfOptions() {
return new Promise((resolve) => { return new Promise((resolve) => {
let index = 0; let index = 0;
(function next(index) { (function next(index) {
if(index>10){ if (index > 10) {
resolve([]) resolve([]);
} }
let arr = dictOptions.value let arr = dictOptions.value;
if (arr && arr.length>0) { if (arr && arr.length > 0) {
resolve(arr) resolve(arr);
} else { } else {
setTimeout(() => { setTimeout(() => {
next(index++) next(index++);
}, 300) }, 300);
} }
})(index) })(index);
}) });
} }
/** /**
* 值改变后 需要往外抛事件 触发下级节点的选项改变 * 值改变后 需要往外抛事件 触发下级节点的选项改变
*/ */
function valueChangeThenEmitNext(value, arr:any=[]){ function valueChangeThenEmitNext(value, arr: any = []) {
if(value && value.length>0){ if (value && value.length > 0) {
if(!arr || arr.length==0){ if (!arr || arr.length == 0) {
arr = dictOptions.value arr = dictOptions.value;
} }
let selected = arr.filter(item=>item.store===value) let selected = arr.filter((item) => item.store === value);
if(selected && selected.length>0){ if (selected && selected.length > 0) {
let id = selected[0].id let id = selected[0].id;
emit('next', id) emit('next', id);
} }
} }
} }
return { return {
selectedValue, selectedValue,
dictOptions, dictOptions,
handleChange handleChange,
} };
} },
}) });
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -1,142 +1,142 @@
<!--popup组件--> <!--popup组件-->
<template> <template>
<div class="components-input-demo-presuffix" v-if="avalid"> <div class="components-input-demo-presuffix" v-if="avalid">
<!--输入框--> <!--输入框-->
<a-input @click="handleOpen" v-model:value="showText" :placeholder="placeholder" readOnly v-bind="attrs"> <a-input @click="handleOpen" v-model:value="showText" :placeholder="placeholder" readOnly v-bind="attrs">
<template #prefix> <template #prefix>
<Icon icon="ant-design:cluster-outlined"></Icon> <Icon icon="ant-design:cluster-outlined"></Icon>
</template> </template>
<template #suffix> <template #suffix>
<Icon icon="ant-design:close-circle-outlined" @click="handleEmpty" title="清空" v-if="showText"></Icon> <Icon icon="ant-design:close-circle-outlined" @click="handleEmpty" title="清空" v-if="showText"></Icon>
</template> </template>
</a-input> </a-input>
<!--popup弹窗--> <!--popup弹窗-->
<JPopupOnlReportModal @register="regModal" :code="code" :multi="multi" :sorter="sorter" :groupId="uniqGroupId" :param="param" @ok="callBack"></JPopupOnlReportModal> <JPopupOnlReportModal @register="regModal" :code="code" :multi="multi" :sorter="sorter" :groupId="uniqGroupId" :param="param" @ok="callBack"></JPopupOnlReportModal>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import JPopupOnlReportModal from './modal/JPopupOnlReportModal.vue' import JPopupOnlReportModal from './modal/JPopupOnlReportModal.vue';
import {defineComponent, ref, reactive, onMounted, watchEffect, watch, computed, unref} from 'vue'; import { defineComponent, ref, reactive, onMounted, watchEffect, watch, computed, unref } from 'vue';
import {useModal} from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import {propTypes} from "/@/utils/propTypes"; import { propTypes } from '/@/utils/propTypes';
import {useAttrs} from "/@/hooks/core/useAttrs"; import { useAttrs } from '/@/hooks/core/useAttrs';
import {useMessage} from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({ export default defineComponent({
name: 'JPopup', name: 'JPopup',
components: { components: {
JPopupOnlReportModal JPopupOnlReportModal,
},
inheritAttrs: false,
props: {
code: propTypes.string.def(''),
value: propTypes.string.def(''),
sorter: propTypes.string.def(''),
width: propTypes.number.def(1200),
placeholder: propTypes.string.def('请选择'),
multi: propTypes.bool.def(false),
param: propTypes.object.def({}),
spliter: propTypes.string.def(','),
groupId: propTypes.string.def(''),
formElRef: propTypes.object,
setFieldsValue: propTypes.func,
fieldConfig: {
type: Array,
default: () => [],
},
},
emits: ['update:value', 'register'],
setup(props, { emit, refs }) {
const { createMessage } = useMessage();
const attrs = useAttrs();
//pop
const avalid = ref(true);
const showText = ref('');
//model
const [regModal, { openModal }] = useModal();
//
let { groupId, code, fieldConfig } = props;
//groupId
const uniqGroupId = computed(() => (groupId ? `${groupId}_${code}_${fieldConfig[0]['source']}_${fieldConfig[0]['target']}` : ''));
/**
* 判断popup配置项是否正确
*/
onMounted(() => {
if (props.fieldConfig.length == 0) {
createMessage.error('popup参数未正确配置!');
avalid.value = false;
}
});
/**
* 监听value数值
*/
watch(
() => props.value,
(val) => {
showText.value = val && val.length > 0 ? val.split(props.spliter).join(',') : '';
}, },
inheritAttrs: false, { immediate: true }
props: { );
code: propTypes.string.def(''),
value: propTypes.string.def(''),
sorter: propTypes.string.def(''),
width: propTypes.number.def(1200),
placeholder: propTypes.string.def('请选择'),
multi: propTypes.bool.def(false),
param: propTypes.object.def({}),
spliter: propTypes.string.def(','),
groupId: propTypes.string.def(''),
formElRef: propTypes.object,
setFieldsValue: propTypes.func,
fieldConfig: {
type: Array,
default: () => []
},
},
emits: ['update:value','register'],
setup(props, {emit, refs}) {
const {createMessage} = useMessage();
const attrs = useAttrs();
//pop
const avalid = ref(true);
const showText = ref('');
//model
const [regModal, {openModal}] = useModal();
//
let {groupId, code, fieldConfig} = props;
//groupId
const uniqGroupId = computed(() => groupId ? `${groupId}_${code}_${fieldConfig[0]['source']}_${fieldConfig[0]['target']}` : '');
/**
* 判断popup配置项是否正确
*/
onMounted(() => {
if (props.fieldConfig.length == 0) {
createMessage.error('popup参数未正确配置!');
avalid.value = false;
}
});
/**
* 监听value数值
*/
watch(
() => props.value,
(val) => {
showText.value = val && val.length>0 ? val.split(props.spliter).join(',') : '';
},
{immediate: true}
);
/** /**
* 打开pop弹出框 * 打开pop弹出框
*/ */
function handleOpen() { function handleOpen() {
!props.disabled && openModal(true); !props.disabled && openModal(true);
} }
/** /**
* TODO 清空 * TODO 清空
*/ */
function handleEmpty() { function handleEmpty() {
showText.value = ''; showText.value = '';
} }
/** /**
* 传值回调 * 传值回调
*/ */
function callBack(rows) { function callBack(rows) {
let {fieldConfig} = props; let { fieldConfig } = props;
//popup //popup
let values = {}; let values = {};
for (let item of fieldConfig) { for (let item of fieldConfig) {
let val = rows.map(row => row[item.source]).join(','); let val = rows.map((row) => row[item.source]).join(',');
item.target.split(",").forEach(target => { item.target.split(',').forEach((target) => {
values[target] = val; values[target] = val;
}); });
} }
// //
props.formElRef && props.formElRef.setFieldsValue(values); props.formElRef && props.formElRef.setFieldsValue(values);
// //
props.setFieldsValue && props.setFieldsValue(values); props.setFieldsValue && props.setFieldsValue(values);
} }
return { return {
showText, showText,
avalid, avalid,
uniqGroupId, uniqGroupId,
attrs, attrs,
regModal, regModal,
handleOpen, handleOpen,
handleEmpty, handleEmpty,
callBack, callBack,
}; };
}, },
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.components-input-demo-presuffix .anticon-close-circle { .components-input-demo-presuffix .anticon-close-circle {
cursor: pointer; cursor: pointer;
color: #ccc; color: #ccc;
transition: color 0.3s; transition: color 0.3s;
font-size: 12px; font-size: 12px;
} }
.components-input-demo-presuffix .anticon-close-circle:hover { .components-input-demo-presuffix .anticon-close-circle:hover {
color: #f5222d; color: #f5222d;
} }
.components-input-demo-presuffix .anticon-close-circle:active { .components-input-demo-presuffix .anticon-close-circle:active {
color: #666; color: #666;
} }
</style> </style>

Some files were not shown because too many files have changed in this diff Show More