diff --git a/.eslintrc.js b/.eslintrc.js index d4c9c6cb..f2dbfa16 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -61,6 +61,7 @@ module.exports = defineConfig({ 'vue/singleline-html-element-content-newline': 'off', 'vue/attribute-hyphenation': 'off', 'vue/require-default-prop': 'off', + 'vue/script-setup-uses-vars': 'off', 'vue/html-self-closing': [ 'error', { diff --git a/CHANGELOG.md b/CHANGELOG.md index 978878f7..f619fee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ -# 1.5 (2021-07-30) +# 1.5.1 (2021-08-07) +### 🐛 Bug Fixes +- 修复windows系统获取项目换行符问题 +- 修复表格分页计算问题 [@Chika99](https://github.com/Chika99) +- 修复锁屏样式自适应问题 [@Chika99](https://github.com/Chika99) +- 依赖 dayjs 移除,用date-fns,和UI框架底层保持一致 +- 修复已知bug + + + +- ### ✨ Features +- 新增 `baseForm` 组件,和`基础`,`useForm`使用方式 +- 新增 `baseModal`,组件,和 `useForm`使用方式 +- 新增`子菜单` new Tag标签 +- 菜单支持 `根路由`配置 + + + + +# 1.5.0 (2021-07-30) ### 🐛 Bug Fixes - 修复表格列配置,拖拽时最后的操作列重复增加 - 多标签页交互优化 @@ -15,7 +34,7 @@ - 本次更新,有破坏性更新,涉及文件重命名,增删调整 -# 1.4 (2021-07-21) +# 1.4.0 (2021-07-21) ### 🐛 Bug Fixes - vite降至2.3.6 - 多标签页交互优化 @@ -27,7 +46,7 @@ - 持续更新更多实用组件及示例,感谢Star -# 1.3 (2021-07-19) +# 1.3.0 (2021-07-19) ### 🐛 Bug Fixes - 修复多标签页左右切换按钮自适应展示 - 修复登录页面出现多标签页 @@ -40,7 +59,7 @@ - 持续更新更多实用组件及示例,感谢Star -# 1.2 (2021-07-16) +# 1.2.0 (2021-07-16) ### 🐛 Bug Fixes - 修复面包屑显示登录页面 - 菜单支持只展开当前父级菜单 @@ -54,7 +73,7 @@ - 持续更新更多实用示例,同时也演示`Naive UI`使用方法 -# 1.1 (2021-07-15) +# 1.1.0 (2021-07-15) - ### ✨ Features - 新增 `基础表单` 示例页面 - 新增 `分步表单` 示例页面 @@ -62,7 +81,7 @@ - 持续更新更多实用示例,同时也演示`Naive UI`使用方法 -# 1.0 (2021-07-12) +# 1.0.0 (2021-07-12) ### 🐛 Bug Fixes - 修复页面切换面包屑未及时更新 diff --git a/build/getConfigFileName.ts b/build/getConfigFileName.ts index 89999784..d61cd416 100644 --- a/build/getConfigFileName.ts +++ b/build/getConfigFileName.ts @@ -3,7 +3,7 @@ * @param env */ export const getConfigFileName = (env: Record) => { - return `__PRODUCTION__${ env.VITE_GLOB_APP_SHORT_NAME || '__APP' }__CONF__` + return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__` .toUpperCase() .replace(/\s/g, ''); }; diff --git a/build/script/buildConf.ts b/build/script/buildConf.ts index 3690b779..a44a1d8e 100644 --- a/build/script/buildConf.ts +++ b/build/script/buildConf.ts @@ -18,19 +18,19 @@ function createConfig( }: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} } ) { try { - const windowConf = `window.${ configName }`; + const windowConf = `window.${configName}`; // Ensure that the variable will not be modified - const configStr = `${ windowConf }=${ JSON.stringify(config) }; - Object.freeze(${ windowConf }); - Object.defineProperty(window, "${ configName }", { + const configStr = `${windowConf}=${JSON.stringify(config)}; + Object.freeze(${windowConf}); + Object.defineProperty(window, "${configName}", { configurable: false, writable: false, }); `.replace(/\s/g, ''); fs.mkdirp(getRootPath(OUTPUT_DIR)); - writeFileSync(getRootPath(`${ OUTPUT_DIR }/${ configFileName }`), configStr); + writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr); - console.log(chalk.cyan(`✨ [${ pkg.name }]`) + ` - configuration file is build successfully:`); + console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`); console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n'); } catch (error) { console.log(chalk.red('configuration file configuration file failed to package:\n' + error)); diff --git a/build/script/postBuild.ts b/build/script/postBuild.ts index e1ac9edf..79c5e8fb 100644 --- a/build/script/postBuild.ts +++ b/build/script/postBuild.ts @@ -14,7 +14,7 @@ export const runBuild = async () => { await runBuildConfig(); } - console.log(`✨ ${ chalk.cyan(`[${ pkg.name }]`) }` + ' - build successfully!'); + console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!'); } catch (error) { console.log(chalk.red('vite build error:\n' + error)); process.exit(1); diff --git a/build/vite/plugin/html.ts b/build/vite/plugin/html.ts index 27968bd0..dec546cd 100644 --- a/build/vite/plugin/html.ts +++ b/build/vite/plugin/html.ts @@ -12,10 +12,10 @@ import { GLOB_CONFIG_FILE_NAME } from '../../constant'; export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) { const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env; - const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${ VITE_PUBLIC_PATH }/`; + const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`; const getAppConfigSrc = () => { - return `${ path || '/' }${ GLOB_CONFIG_FILE_NAME }?v=${ pkg.version }-${ new Date().getTime() }`; + return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`; }; const htmlPlugin: Plugin[] = html({ @@ -28,13 +28,13 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) { // Embed the generated app.config.js file tags: isBuild ? [ - { - tag: 'script', - attrs: { - src: getAppConfigSrc(), + { + tag: 'script', + attrs: { + src: getAppConfigSrc(), + }, }, - }, - ] + ] : [], }, }); diff --git a/build/vite/plugin/styleImport.ts b/build/vite/plugin/styleImport.ts index eccda21b..f09d35df 100644 --- a/build/vite/plugin/styleImport.ts +++ b/build/vite/plugin/styleImport.ts @@ -13,7 +13,7 @@ export function configStyleImportPlugin(isBuild: boolean) { libraryName: 'ant-design-vue', esModule: true, resolveStyle: (name) => { - return `ant-design-vue/es/${ name }/style/index`; + return `ant-design-vue/es/${name}/style/index`; }, }, ], diff --git a/mock/table/list.ts b/mock/table/list.ts index f701a885..4593e686 100644 --- a/mock/table/list.ts +++ b/mock/table/list.ts @@ -5,7 +5,7 @@ const tableList = (pageSize) => { const result: any[] = []; doCustomTimes(pageSize, () => { result.push({ - id: '@integer(10,100)', + id: '@integer(10,999999)', beginTime: '@datetime', endTime: '@datetime', address: '@city()', diff --git a/package.json b/package.json index d420a387..528c3d2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "naive-ui-admin", - "version": "1.5.0", + "version": "1.5.1", "author": { "name": "Ahjung", "email": "735878602@qq.com", @@ -30,7 +30,7 @@ "@vueuse/core": "^5.0.3", "axios": "^0.21.1", "blueimp-md5": "^2.18.0", - "dayjs": "^1.10.5", + "date-fns": "^2.23.0", "echarts": "^5.1.2", "element-resize-detector": "^1.2.3", "lodash": "^4.17.21", @@ -38,7 +38,7 @@ "makeit-captcha": "^1.2.5", "mitt": "^2.1.0", "mockjs": "^1.1.0", - "naive-ui": "^2.15.11", + "naive-ui": "^2.16.0", "pinia": "^2.0.0-beta.3", "qs": "^6.10.1", "vfonts": "^0.1.0", diff --git a/prettier.config.js b/prettier.config.js index 0c20bc90..b4e993ae 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -15,6 +15,6 @@ module.exports = { requirePragma: false, proseWrap: 'never', htmlWhitespaceSensitivity: 'strict', - endOfLine: 'lf', + endOfLine: 'auto', rangeStart: 0, }; diff --git a/src/App.vue b/src/App.vue index 0e9be034..e58a1e4a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -24,6 +24,7 @@ import { useLockscreenStore } from '@/store/modules/lockscreen'; import { useRoute } from 'vue-router'; import { useDesignSettingStore } from '@/store/modules/designSetting'; + import { lighten } from '@/utils/index'; export default defineComponent({ name: 'App', @@ -35,14 +36,20 @@ const isLock = computed(() => useLockscreen.isLock); const lockTime = computed(() => useLockscreen.lockTime); + /** + * @type import('naive-ui').GlobalThemeOverrides + */ const getThemeOverrides = computed(() => { + const appTheme = designStore.appTheme; + const lightenStr = lighten(designStore.appTheme, 6); return { common: { - primaryColor: designStore.appTheme, - primaryColorHover: '#57a3f3', + primaryColor: appTheme, + primaryColorHover: lightenStr, + primaryColorPressed: lightenStr, }, LoadingBar: { - colorLoading: designStore.appTheme, + colorLoading: appTheme, }, }; }); diff --git a/src/components/Form/index.ts b/src/components/Form/index.ts new file mode 100644 index 00000000..0f37df5d --- /dev/null +++ b/src/components/Form/index.ts @@ -0,0 +1,4 @@ +export { default as BasicForm } from './src/BasicForm.vue'; +export { useForm } from './src/hooks/useForm'; +export * from './src/types/form'; +export * from './src/types/index'; diff --git a/src/components/Form/src/BasicForm.vue b/src/components/Form/src/BasicForm.vue new file mode 100644 index 00000000..ea40df06 --- /dev/null +++ b/src/components/Form/src/BasicForm.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/src/components/Form/src/helper.ts b/src/components/Form/src/helper.ts new file mode 100644 index 00000000..b0af3c43 --- /dev/null +++ b/src/components/Form/src/helper.ts @@ -0,0 +1,42 @@ +import { ComponentType } from '/types/index'; + +/** + * @description: 生成placeholder + */ +export function createPlaceholderMessage(component: ComponentType) { + if (component === 'NInput') return '请输入'; + if ( + ['NPicker', 'NSelect', 'NCheckbox', 'NRadio', 'NSwitch', 'NDatePicker', 'NTimePicker'].includes( + component + ) + ) + return '请选择'; + return ''; +} + +const DATE_TYPE = ['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker']; + +function genType() { + return [...DATE_TYPE, 'RangePicker']; +} + +/** + * 时间字段 + */ +export const dateItemType = genType(); + +export function defaultType(component) { + if (component === 'NInput') return ''; + if (component === 'NInputNumber') return null; + return [ + 'NPicker', + 'NSelect', + 'NCheckbox', + 'NRadio', + 'NSwitch', + 'NDatePicker', + 'NTimePicker', + ].includes(component) + ? '' + : undefined; +} diff --git a/src/components/Form/src/hooks/useForm.ts b/src/components/Form/src/hooks/useForm.ts new file mode 100644 index 00000000..b87dd869 --- /dev/null +++ b/src/components/Form/src/hooks/useForm.ts @@ -0,0 +1,87 @@ +import type { FormProps, FormActionType, UseFormReturnType } from '../types/form'; +// @ts-ignore +import type { DynamicProps } from '/#/utils'; + +import { ref, onUnmounted, unref, nextTick, watch } from 'vue'; +import { isProdMode } from '@/utils/env'; +import { getDynamicProps } from '@/utils'; + +type Props = Partial>; + +export function useForm(props?: Props): UseFormReturnType { + const formRef = ref>(null); + const loadedRef = ref>(false); + + async function getForm() { + const form = unref(formRef); + if (!form) { + console.error( + '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; + } + + function register(instance: FormActionType) { + isProdMode() && + onUnmounted(() => { + formRef.value = null; + loadedRef.value = null; + }); + if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return; + + formRef.value = instance; + loadedRef.value = true; + + watch( + () => props, + () => { + props && instance.setProps(getDynamicProps(props)); + }, + { + immediate: true, + deep: true, + } + ); + } + + const methods: FormActionType = { + setProps: async (formProps: Partial) => { + const form = await getForm(); + await form.setProps(formProps); + }, + + resetFields: async () => { + getForm().then(async (form) => { + await form.resetFields(); + }); + }, + + clearValidate: async (name?: string | string[]) => { + const form = await getForm(); + await form.clearValidate(name); + }, + + getFieldsValue: () => { + return unref(formRef)?.getFieldsValue() as T; + }, + + setFieldsValue: async (values: T) => { + const form = await getForm(); + await form.setFieldsValue(values); + }, + + submit: async (): Promise => { + const form = await getForm(); + return form.submit(); + }, + + validate: async (nameList?: any[]): Promise => { + const form = await getForm(); + return form.validate(nameList); + }, + }; + + return [register, methods]; +} diff --git a/src/components/Form/src/hooks/useFormContext.ts b/src/components/Form/src/hooks/useFormContext.ts new file mode 100644 index 00000000..2d2a212b --- /dev/null +++ b/src/components/Form/src/hooks/useFormContext.ts @@ -0,0 +1,11 @@ +import { provide, inject } from 'vue'; + +const key = Symbol('formElRef'); + +export function createFormContext(instance) { + provide(key, instance); +} + +export function useFormContext() { + return inject(key); +} diff --git a/src/components/Form/src/hooks/useFormEvents.ts b/src/components/Form/src/hooks/useFormEvents.ts new file mode 100644 index 00000000..dad92b51 --- /dev/null +++ b/src/components/Form/src/hooks/useFormEvents.ts @@ -0,0 +1,107 @@ +import type { ComputedRef, Ref } from 'vue'; +import type { FormProps, FormSchema, FormActionType } from '../types/form'; +import { unref, toRaw } from 'vue'; +import { isFunction } from '@/utils/is'; + +declare type EmitType = (event: string, ...args: any[]) => void; + +interface UseFormActionContext { + emit: EmitType; + getProps: ComputedRef; + getSchema: ComputedRef; + formModel: Recordable; + formElRef: Ref; + defaultFormModel: Recordable; + loadingSub: Ref; + handleFormValues: Function; +} + +export function useFormEvents({ + emit, + getProps, + formModel, + getSchema, + formElRef, + defaultFormModel, + loadingSub, + handleFormValues, +}: UseFormActionContext) { + // 验证 + async function validate() { + return unref(formElRef)?.validate(); + } + + // 提交 + async function handleSubmit(e?: Event): Promise { + e && e.preventDefault(); + loadingSub.value = true; + const { submitFunc } = unref(getProps); + if (submitFunc && isFunction(submitFunc)) { + await submitFunc(); + return; + } + const formEl = unref(formElRef); + if (!formEl) return; + try { + await validate(); + loadingSub.value = false; + emit('submit', formModel); + return true; + } catch (error) { + loadingSub.value = false; + return false; + } + } + + //清空校验 + async function clearValidate() { + // @ts-ignore + await unref(formElRef)?.restoreValidation(); + } + + //重置 + async function resetFields(): Promise { + const { resetFunc, submitOnReset } = unref(getProps); + resetFunc && isFunction(resetFunc) && (await resetFunc()); + + const formEl = unref(formElRef); + if (!formEl) return; + Object.keys(formModel).forEach((key) => { + formModel[key] = unref(defaultFormModel)[key] || null; + }); + await clearValidate(); + const fromValues = handleFormValues(toRaw(unref(formModel))); + emit('reset', fromValues); + submitOnReset && (await handleSubmit()); + } + + //获取表单值 + function getFieldsValue(): Recordable { + const formEl = unref(formElRef); + if (!formEl) return {}; + return handleFormValues(toRaw(unref(formModel))); + } + + //设置表单字段值 + async function setFieldsValue(values: Recordable): Promise { + const fields = unref(getSchema) + .map((item) => item.field) + .filter(Boolean); + + Object.keys(values).forEach((key) => { + const value = values[key]; + if (fields.includes(key)) { + formModel[key] = value; + } + }); + } + + return { + handleSubmit, + validate, + resetFields, + getFieldsValue, + clearValidate, + setFieldsValue, + }; +} diff --git a/src/components/Form/src/hooks/useFormValues.ts b/src/components/Form/src/hooks/useFormValues.ts new file mode 100644 index 00000000..2d45bbe9 --- /dev/null +++ b/src/components/Form/src/hooks/useFormValues.ts @@ -0,0 +1,54 @@ +import { isArray, isFunction, isObject, isString, isNullOrUnDef } from '@/utils/is'; +import { unref } from 'vue'; +import type { Ref, ComputedRef } from 'vue'; +import type { FormSchema } from '../types/form'; +import { set } from 'lodash-es'; + +interface UseFormValuesContext { + defaultFormModel: Ref; + getSchema: ComputedRef; + formModel: Recordable; +} +export function useFormValues({ defaultFormModel, getSchema, formModel }: UseFormValuesContext) { + // 加工 form values + function handleFormValues(values: Recordable) { + if (!isObject(values)) { + return {}; + } + const res: Recordable = {}; + for (const item of Object.entries(values)) { + let [, value] = item; + const [key] = item; + if ( + !key || + (isArray(value) && value.length === 0) || + isFunction(value) || + isNullOrUnDef(value) + ) { + continue; + } + // 删除空格 + if (isString(value)) { + value = value.trim(); + } + set(res, key, value); + } + return res; + } + + //初始化默认值 + function initDefault() { + const schemas = unref(getSchema); + const obj: Recordable = {}; + schemas.forEach((item) => { + const { defaultValue } = item; + if (!isNullOrUnDef(defaultValue)) { + obj[item.field] = defaultValue; + formModel[item.field] = defaultValue; + } + }); + defaultFormModel.value = obj; + } + + return { handleFormValues, initDefault }; +} diff --git a/src/components/Form/src/props.ts b/src/components/Form/src/props.ts new file mode 100644 index 00000000..46582e7d --- /dev/null +++ b/src/components/Form/src/props.ts @@ -0,0 +1,82 @@ +import type { CSSProperties, PropType } from 'vue'; +import { FormSchema } from './types/form'; +import type { GridProps, GridItemProps } from 'naive-ui/lib/grid'; +import type { ButtonProps } from 'naive-ui/lib/button'; +import { propTypes } from '@/utils/propTypes'; +export const basicProps = { + // 标签宽度 固定宽度 + labelWidth: { + type: [Number, String] as PropType, + default: 80, + }, + // 表单配置规则 + schemas: { + type: [Array] as PropType, + default: () => [], + }, + //布局方式 + layout: { + type: String, + default: 'inline', + }, + //是否展示为行内表单 + inline: { + type: Boolean, + default: false, + }, + //大小 + size: { + type: String, + default: 'medium', + }, + //标签位置 + labelPlacement: { + type: String, + default: 'left', + }, + //组件是否width 100% + isFull: { + type: Boolean, + default: true, + }, + //是否显示操作按钮(查询/重置) + showActionButtonGroup: propTypes.bool.def(true), + // 显示重置按钮 + showResetButton: propTypes.bool.def(true), + //重置按钮配置 + resetButtonOptions: Object as PropType>, + // 显示确认按钮 + showSubmitButton: propTypes.bool.def(true), + // 确认按钮配置 + submitButtonOptions: Object as PropType>, + //展开收起按钮 + showAdvancedButton: propTypes.bool.def(true), + // 确认按钮文字 + submitButtonText: { + type: String, + default: '查询', + }, + //重置按钮文字 + resetButtonText: { + type: String, + default: '重置', + }, + //grid 配置 + gridProps: Object as PropType, + //gi配置 + giProps: Object as PropType, + //grid 样式 + baseGridStyle: { + type: Object as PropType, + }, + //是否折叠 + collapsed: { + type: Boolean, + default: false, + }, + //默认展示的行数 + collapsedRows: { + type: Number, + default: 1, + }, +}; diff --git a/src/components/Form/src/types/form.ts b/src/components/Form/src/types/form.ts new file mode 100644 index 00000000..5d551119 --- /dev/null +++ b/src/components/Form/src/types/form.ts @@ -0,0 +1,58 @@ +import { ComponentType } from './index'; +import type { CSSProperties } from 'vue'; +import type { GridProps, GridItemProps } from 'naive-ui/lib/grid'; +import type { ButtonProps } from 'naive-ui/lib/button'; + +export interface FormSchema { + field: string; + label: string; + labelMessage?: string; + labelMessageStyle?: object | string; + defaultValue?: any; + component?: ComponentType; + componentProps?: object; + slot?: string; + rules?: object | object[]; + giProps?: GridItemProps; + isFull?: boolean; + suffix?: string; +} + +export interface FormProps { + model?: Recordable; + labelWidth?: number | string; + schemas?: FormSchema[]; + inline: boolean; + layout?: string; + size: string; + labelPlacement: string; + isFull: boolean; + showActionButtonGroup?: boolean; + showResetButton?: boolean; + resetButtonOptions?: Partial; + showSubmitButton?: boolean; + showAdvancedButton?: boolean; + submitButtonOptions?: Partial; + submitButtonText?: string; + resetButtonText?: string; + gridProps?: GridProps; + giProps?: GridItemProps; + resetFunc?: () => Promise; + submitFunc?: () => Promise; + submitOnReset?: boolean; + baseGridStyle?: CSSProperties; +} + +export interface FormActionType { + submit: () => Promise; + setProps: (formProps: Partial) => Promise; + setFieldsValue: (values: T) => Promise; + clearValidate: (name?: string | string[]) => Promise; + getFieldsValue: () => Recordable; + resetFields: () => Promise; + validate: (nameList?: any[]) => Promise; +} + +export type RegisterFn = (formInstance: FormActionType) => void; + +export type UseFormReturnType = [RegisterFn, FormActionType]; diff --git a/src/components/Form/src/types/index.ts b/src/components/Form/src/types/index.ts new file mode 100644 index 00000000..5cb0baa0 --- /dev/null +++ b/src/components/Form/src/types/index.ts @@ -0,0 +1,28 @@ +export type ComponentType = + | 'NInput' + | 'NInputGroup' + | 'NInputPassword' + | 'NInputSearch' + | 'NInputTextArea' + | 'NInputNumber' + | 'NInputCountDown' + | 'NSelect' + | 'NTreeSelect' + | 'NRadioButtonGroup' + | 'NRadioGroup' + | 'NCheckbox' + | 'NCheckboxGroup' + | 'NAutoComplete' + | 'NCascader' + | 'NDatePicker' + | 'NMonthPicker' + | 'NRangePicker' + | 'NWeekPicker' + | 'NTimePicker' + | 'NSwitch' + | 'NStrengthMeter' + | 'NUpload' + | 'NIconPicker' + | 'NRender' + | 'NSlider' + | 'NRate'; diff --git a/src/components/Modal/index.ts b/src/components/Modal/index.ts new file mode 100644 index 00000000..586a945e --- /dev/null +++ b/src/components/Modal/index.ts @@ -0,0 +1,3 @@ +export { default as basicModal } from './src/basicModal.vue'; +export { useModal } from './src/hooks/useModal'; +export * from './src/type'; diff --git a/src/components/Modal/src/basicModal.vue b/src/components/Modal/src/basicModal.vue new file mode 100644 index 00000000..884db8e5 --- /dev/null +++ b/src/components/Modal/src/basicModal.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/src/components/Modal/src/hooks/useModal.ts b/src/components/Modal/src/hooks/useModal.ts new file mode 100644 index 00000000..5b4e69c7 --- /dev/null +++ b/src/components/Modal/src/hooks/useModal.ts @@ -0,0 +1,57 @@ +import { ref, onUnmounted, unref, getCurrentInstance, watch } from 'vue'; +import { isProdMode } from '@/utils/env'; +import { UseModalReturnType, ModalMethods } from './type'; +import { getDynamicProps } from '@/utils'; +export function useModal(props?: Props): UseModalReturnType { + const modal = ref>(null); + const loaded = ref>(false); + + function register(modalMethod: ModalMethods) { + if (!getCurrentInstance()) { + throw new Error('useModal() can only be used inside setup() or functional components!'); + } + isProdMode() && + onUnmounted(() => { + modal.value = null; + loaded.value = false; + }); + if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return; + modal.value = modalMethod; + + watch( + () => props, + () => { + const { setProps } = modal.value; + props && setProps(getDynamicProps(props)); + }, + { + immediate: true, + deep: true, + } + ); + } + + const getInstance = () => { + const instance = unref(modal); + if (!instance) { + error('useModal instance is undefined!'); + } + return instance; + }; + + const methods: ReturnMethods = { + setProps: (props: Partial): void => { + getInstance()?.setProps(props); + }, + openModal: () => { + getInstance()?.openModal(); + }, + closeModal: () => { + getInstance()?.closeModal(); + }, + setSubLoading: () => { + getInstance()?.setSubLoading(); + }, + }; + return [register, methods]; +} diff --git a/src/components/Modal/src/props.ts b/src/components/Modal/src/props.ts new file mode 100644 index 00000000..9fafa12c --- /dev/null +++ b/src/components/Modal/src/props.ts @@ -0,0 +1,30 @@ +import { NModal } from 'naive-ui'; + +export const basicProps = { + ...NModal.props, + // 确认按钮文字 + subBtuText: { + type: String, + default: '确认', + }, + showIcon: { + type: Boolean, + default: false, + }, + width: { + type: Number, + default: 446, + }, + title: { + type: String, + default: '', + }, + maskClosable: { + type: Boolean, + default: false, + }, + preset: { + type: String, + default: 'dialog', + }, +}; diff --git a/src/components/Modal/src/type/index.ts b/src/components/Modal/src/type/index.ts new file mode 100644 index 00000000..8f7029ac --- /dev/null +++ b/src/components/Modal/src/type/index.ts @@ -0,0 +1,12 @@ +export interface ModalProps { + subBtuText?: string; +} + +/** + * @description: 弹窗对外暴露的方法 + */ +export interface ModalMethods { + setProps: (props: Partial) => void; + openModal: () => void; + closeModal: () => void; +} diff --git a/src/components/Table/src/Table.vue b/src/components/Table/src/Table.vue index 2601f415..0cf3692d 100644 --- a/src/components/Table/src/Table.vue +++ b/src/components/Table/src/Table.vue @@ -73,7 +73,6 @@ + + diff --git a/src/views/comp/modal/index.vue b/src/views/comp/modal/index.vue new file mode 100644 index 00000000..976926bd --- /dev/null +++ b/src/views/comp/modal/index.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/src/views/comp/table/basic.vue b/src/views/comp/table/basic.vue index 6c364988..936a01dc 100644 --- a/src/views/comp/table/basic.vue +++ b/src/views/comp/table/basic.vue @@ -38,7 +38,7 @@ actionColumn: { width: 150, title: '操作', - dataIndex: 'action', + key: 'action', fixed: 'right', align: 'center', render(record) { diff --git a/src/views/comp/table/editCell.vue b/src/views/comp/table/editCell.vue index 07dc2eee..c9d92967 100644 --- a/src/views/comp/table/editCell.vue +++ b/src/views/comp/table/editCell.vue @@ -38,7 +38,7 @@ actionColumn: { width: 150, title: '操作', - dataIndex: 'action', + key: 'action', fixed: 'right', align: 'center', render(record) { @@ -61,7 +61,7 @@ } function onEditChange({ column, value, record }) { - if (column.dataIndex === 'id') { + if (column.key === 'id') { record.editValueRefs.name4.value = `${value}`; } console.log(column, value, record); diff --git a/src/views/comp/table/editRow.vue b/src/views/comp/table/editRow.vue index 90e44e5f..316c3a9d 100644 --- a/src/views/comp/table/editRow.vue +++ b/src/views/comp/table/editRow.vue @@ -61,7 +61,7 @@ } function onEditChange({ column, value, record }) { - if (column.dataIndex === 'id') { + if (column.key === 'id') { record.editValueRefs.name4.value = `${value}`; } console.log(column, value, record); diff --git a/src/views/form/stepForm/Step1.vue b/src/views/form/stepForm/Step1.vue index 99750b04..673f673d 100644 --- a/src/views/form/stepForm/Step1.vue +++ b/src/views/form/stepForm/Step1.vue @@ -5,7 +5,7 @@ :rules="rules" label-placement="left" ref="form1Ref" - style="max-width: 500px; margin: 40px auto 0" + style="max-width: 500px; margin: 40px auto 0 80px" > NaiveUiAdmin@163.com diff --git a/src/views/form/stepForm/stepForm.vue b/src/views/form/stepForm/stepForm.vue index 66d95083..67edf4df 100644 --- a/src/views/form/stepForm/stepForm.vue +++ b/src/views/form/stepForm/stepForm.vue @@ -6,11 +6,11 @@ - + - + diff --git a/src/views/list/basicList/index.vue b/src/views/list/basicList/index.vue index 19cc7cb7..812f7744 100644 --- a/src/views/list/basicList/index.vue +++ b/src/views/list/basicList/index.vue @@ -1,5 +1,11 @@ - - @@ -59,9 +61,10 @@