diff --git a/.eslintrc.js b/.eslintrc.js index 72d4664b..f2977df6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,7 +8,6 @@ module.exports = defineConfig({ es6: true }, parser: 'vue-eslint-parser', - plugins: ['vue'], parserOptions: { parser: '@typescript-eslint/parser', ecmaVersion: 2020, @@ -26,9 +25,9 @@ module.exports = defineConfig({ '@unocss' ], rules: { + 'vue/no-setup-props-destructure': 'off', 'vue/script-setup-uses-vars': 'error', 'vue/no-reserved-component-names': 'off', - 'vue/no-setup-props-destructure': 'off', '@typescript-eslint/ban-ts-ignore': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/no-explicit-any': 'off', @@ -54,6 +53,7 @@ module.exports = defineConfig({ 'vue/attribute-hyphenation': 'off', 'vue/require-default-prop': 'off', 'vue/require-explicit-emits': 'off', + 'vue/require-toggle-inside-transition': 'off', 'vue/html-self-closing': [ 'error', { diff --git a/.vscode/settings.json b/.vscode/settings.json index 45156944..1d3f0aa8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "typescript.tsdk": "./node_modules/typescript/lib", + "typescript.tsdk": "node_modules/typescript/lib", "npm.packageManager": "pnpm", "editor.tabSize": 2, "prettier.printWidth": 100, // 超过最大值换行 @@ -86,15 +86,12 @@ "source.fixAll.eslint": true }, "[vue]": { - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true, - "source.fixAll.stylelint": true - } + "editor.defaultFormatter": "rvest.vs-code-prettier-eslint" }, "i18n-ally.localesPaths": ["src/locales"], "i18n-ally.keystyle": "nested", "i18n-ally.sortKeys": true, - "i18n-ally.namespace": true, + "i18n-ally.namespace": false, "i18n-ally.enabledParsers": ["ts"], "i18n-ally.sourceLanguage": "en", "i18n-ally.displayLanguage": "zh-CN", diff --git a/package.json b/package.json index e81fe550..21a731a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yudao-ui-admin-vue3", - "version": "1.8.2-snapshot", + "version": "1.8.3-snapshot", "description": "基于vue3、vite4、element-plus、typesScript", "author": "xingyu", "private": false, @@ -41,12 +41,12 @@ "bpmn-js-token-simulation": "^0.10.0", "camunda-bpmn-moddle": "^7.0.1", "cropperjs": "^1.6.1", - "crypto-js": "^4.1.1", + "crypto-js": "^4.2.0", "dayjs": "^1.11.10", - "diagram-js": "^12.4.0", + "diagram-js": "^12.5.0", "echarts": "^5.4.3", "echarts-wordcloud": "^2.1.0", - "element-plus": "2.4.0", + "element-plus": "2.4.1", "fast-xml-parser": "^4.3.2", "highlight.js": "^11.9.0", "intro.js": "^7.2.0", @@ -62,7 +62,7 @@ "steady-xml": "^0.1.0", "url": "^0.11.3", "video.js": "^7.21.5", - "vue": "^3.3.4", + "vue": "^3.3.7", "vue-dompurify-html": "^4.1.4", "vue-i18n": "^9.5.0", "vue-router": "^4.2.5", @@ -72,55 +72,54 @@ "xml-js": "^1.6.11" }, "devDependencies": { - "@commitlint/cli": "^17.8.0", - "@commitlint/config-conventional": "^17.8.0", - "@iconify/json": "^2.2.129", + "@commitlint/cli": "^18.0.0", + "@commitlint/config-conventional": "^18.0.0", + "@iconify/json": "^2.2.132", "@intlify/unplugin-vue-i18n": "^1.4.0", "@purge-icons/generated": "^0.9.0", - "@types/intro.js": "^5.1.2", - "@types/lodash-es": "^4.17.9", - "@types/node": "^20.8.6", - "@types/nprogress": "^0.2.1", - "@types/qrcode": "^1.5.2", - "@types/qs": "^6.9.8", + "@types/intro.js": "^5.1.3", + "@types/lodash-es": "^4.17.10", + "@types/node": "^20.8.8", + "@types/nprogress": "^0.2.2", + "@types/qrcode": "^1.5.4", + "@types/qs": "^6.9.9", "@types/sortablejs": "^1.15.4", - "@typescript-eslint/eslint-plugin": "^6.7.5", - "@typescript-eslint/parser": "^6.7.5", - "@unocss/transformer-variant-group": "^0.56.5", - "@unocss/eslint-config": "^0.56.5", + "@typescript-eslint/eslint-plugin": "^6.9.0", + "@typescript-eslint/parser": "^6.9.0", + "@unocss/transformer-variant-group": "^0.57.1", + "@unocss/eslint-config": "^0.57.1", "@vitejs/plugin-legacy": "^4.1.1", "@vitejs/plugin-vue": "^4.4.0", "@vitejs/plugin-vue-jsx": "^3.0.2", - "@vue-macros/volar": "^0.17.0", "autoprefixer": "^10.4.16", "bpmn-js": "8.9.0", "bpmn-js-properties-panel": "0.46.0", "consola": "^3.2.3", - "eslint": "^8.51.0", + "eslint": "^8.52.0", "eslint-config-prettier": "^9.0.0", "eslint-define-config": "^1.24.1", "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-vue": "^9.17.0", - "lint-staged": "^15.0.1", + "eslint-plugin-vue": "^9.18.0", + "lint-staged": "^15.0.2", "postcss": "^8.4.31", "postcss-html": "^1.5.0", "postcss-scss": "^4.0.9", "prettier": "^3.0.3", "rimraf": "^5.0.5", "rollup": "^4.1.4", - "sass": "^1.69.3", - "stylelint": "^15.10.3", + "sass": "^1.69.4", + "stylelint": "^15.11.0", "stylelint-config-html": "^1.1.0", "stylelint-config-recommended": "^13.0.0", "stylelint-config-standard": "^34.0.0", "stylelint-order": "^6.0.3", - "terser": "^5.21.0", + "terser": "^5.22.0", "typescript": "5.2.2", - "unocss": "^0.56.5", + "unocss": "^0.57.1", "unplugin-auto-import": "^0.16.6", "unplugin-element-plus": "^0.8.0", "unplugin-vue-components": "^0.25.2", - "vite": "4.4.11", + "vite": "4.5.0", "vite-plugin-compression": "^0.5.1", "vite-plugin-ejs": "^1.6.4", "vite-plugin-eslint": "^1.8.1", @@ -129,7 +128,7 @@ "vite-plugin-svg-icons": "^2.0.1", "vite-plugin-top-level-await": "^1.3.1", "vue-eslint-parser": "^9.3.2", - "vue-tsc": "^1.8.19" + "vue-tsc": "^1.8.20" }, "license": "MIT", "repository": { diff --git a/src/api/crm/customer/index.ts b/src/api/crm/customer/index.ts new file mode 100644 index 00000000..0cbba590 --- /dev/null +++ b/src/api/crm/customer/index.ts @@ -0,0 +1,56 @@ +import request from '@/config/axios' + +export interface CustomerVO { + id: number + name: string + industryId: number + level: number + source: number + followUpStatus: boolean + lockStatus: boolean + mobile: string + telephone: string + website: string + qq: string + wechat: string + email: string + description: string + remark: string + ownerUserId: number + roUserIds: string + rwUserIds: string + areaId: number + detailAddress: string + contactLastTime: Date + contactNextTime: Date +} + +// 查询客户列表 +export const getCustomerPage = async (params) => { + return await request.get({ url: `/crm/customer/page`, params }) +} + +// 查询客户详情 +export const getCustomer = async (id: number) => { + return await request.get({ url: `/crm/customer/get?id=` + id }) +} + +// 新增客户 +export const createCustomer = async (data: CustomerVO) => { + return await request.post({ url: `/crm/customer/create`, data }) +} + +// 修改客户 +export const updateCustomer = async (data: CustomerVO) => { + return await request.put({ url: `/crm/customer/update`, data }) +} + +// 删除客户 +export const deleteCustomer = async (id: number) => { + return await request.delete({ url: `/crm/customer/delete?id=` + id }) +} + +// 导出客户 Excel +export const exportCustomer = async (params) => { + return await request.download({ url: `/crm/customer/export-excel`, params }) +} diff --git a/src/api/mall/promotion/diy/page.ts b/src/api/mall/promotion/diy/page.ts new file mode 100644 index 00000000..255015d2 --- /dev/null +++ b/src/api/mall/promotion/diy/page.ts @@ -0,0 +1,35 @@ +import request from '@/config/axios' + +export interface DiyPageVO { + id?: number + templateId?: number + name: string + remark: string + previewImageUrls: string[] + property: string +} + +// 查询装修页面列表 +export const getDiyPagePage = async (params: any) => { + return await request.get({ url: `/promotion/diy-page/page`, params }) +} + +// 查询装修页面详情 +export const getDiyPage = async (id: number) => { + return await request.get({ url: `/promotion/diy-page/get?id=` + id }) +} + +// 新增装修页面 +export const createDiyPage = async (data: DiyPageVO) => { + return await request.post({ url: `/promotion/diy-page/create`, data }) +} + +// 修改装修页面 +export const updateDiyPage = async (data: DiyPageVO) => { + return await request.put({ url: `/promotion/diy-page/update`, data }) +} + +// 删除装修页面 +export const deleteDiyPage = async (id: number) => { + return await request.delete({ url: `/promotion/diy-page/delete?id=` + id }) +} diff --git a/src/api/mall/promotion/diy/template.ts b/src/api/mall/promotion/diy/template.ts new file mode 100644 index 00000000..72eea41c --- /dev/null +++ b/src/api/mall/promotion/diy/template.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +export interface DiyTemplateVO { + id?: number + name: string + used: boolean + usedTime?: Date + remark: string + previewImageUrls: string[] + property: string +} + +// 查询装修模板列表 +export const getDiyTemplatePage = async (params: any) => { + return await request.get({ url: `/promotion/diy-template/page`, params }) +} + +// 查询装修模板详情 +export const getDiyTemplate = async (id: number) => { + return await request.get({ url: `/promotion/diy-template/get?id=` + id }) +} + +// 新增装修模板 +export const createDiyTemplate = async (data: DiyTemplateVO) => { + return await request.post({ url: `/promotion/diy-template/create`, data }) +} + +// 修改装修模板 +export const updateDiyTemplate = async (data: DiyTemplateVO) => { + return await request.put({ url: `/promotion/diy-template/update`, data }) +} + +// 删除装修模板 +export const deleteDiyTemplate = async (id: number) => { + return await request.delete({ url: `/promotion/diy-template/delete?id=` + id }) +} + +// 使用装修模板 +export const useDiyTemplate = async (id: number) => { + return await request.put({ url: `/promotion/diy-template/use?id=` + id }) +} diff --git a/src/assets/imgs/diy/statusBar.png b/src/assets/imgs/diy/statusBar.png new file mode 100644 index 00000000..b85562e4 Binary files /dev/null and b/src/assets/imgs/diy/statusBar.png differ diff --git a/src/components/ColorInput/index.vue b/src/components/ColorInput/index.vue new file mode 100644 index 00000000..abd083a1 --- /dev/null +++ b/src/components/ColorInput/index.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/components/DiyEditor/components/ComponentLibrary.vue b/src/components/DiyEditor/components/ComponentLibrary.vue new file mode 100644 index 00000000..8e918fa9 --- /dev/null +++ b/src/components/DiyEditor/components/ComponentLibrary.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/Carousel/config.ts b/src/components/DiyEditor/components/mobile/Carousel/config.ts new file mode 100644 index 00000000..6c790186 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/Carousel/config.ts @@ -0,0 +1,44 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 轮播图属性 */ +export interface CarouselProperty { + // 选择模板 + swiperType: number + // 图片圆角 + borderRadius: number + // 页面边距 + pageMargin: number + // 图片边距 + imageMargin: number + // 分页类型 + pagingType: 'bullets' | 'fraction' | 'progressbar' + // 一行个数 + rowIndividual: number + // 添加图片 + items: CarouselItemProperty[] +} + +export interface CarouselItemProperty { + title: string + imgUrl: string + link: string +} + +// 定义组件 +export const component = { + id: 'Carousel', + name: '轮播图', + icon: 'system-uicons:carousel', + property: { + swiperType: 0, // 选择模板 + borderRadius: 0, // 图片圆角 + pageMargin: 0, // 页面边距 + imageMargin: 0, // 图片边距 + pagingType: 'bullets', // 分页类型 + rowIndividual: 2, // 一行个数 + items: [ + { imgUrl: 'https://static.iocoder.cn/mall/banner-01.jpg' }, + { imgUrl: 'https://static.iocoder.cn/mall/banner-02.jpg' } + ] as CarouselItemProperty[] + } +} as DiyComponent diff --git a/src/components/DiyEditor/components/mobile/Carousel/index.vue b/src/components/DiyEditor/components/mobile/Carousel/index.vue new file mode 100644 index 00000000..e9a0ab39 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/Carousel/index.vue @@ -0,0 +1,75 @@ + + + + diff --git a/src/components/DiyEditor/components/mobile/Carousel/property.vue b/src/components/DiyEditor/components/mobile/Carousel/property.vue new file mode 100644 index 00000000..acaee35f --- /dev/null +++ b/src/components/DiyEditor/components/mobile/Carousel/property.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/Divider/config.ts b/src/components/DiyEditor/components/mobile/Divider/config.ts new file mode 100644 index 00000000..9b553604 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/Divider/config.ts @@ -0,0 +1,29 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 分割线属性 */ +export interface DividerProperty { + // 高度 + height: number + // 线宽 + lineWidth: number + // 边距类型 + paddingType: 'none' | 'horizontal' + // 颜色 + lineColor: string + // 类型 + borderType: 'solid' | 'dashed' | 'dotted' | 'none' +} + +// 定义组件 +export const component = { + id: 'Divider', + name: '分割线', + icon: 'tdesign:component-divider-vertical', + property: { + height: 30, + lineWidth: 1, + paddingType: 'none', + lineColor: '#dcdfe6', + borderType: 'solid' + } +} as DiyComponent diff --git a/src/components/DiyEditor/components/mobile/Divider/index.vue b/src/components/DiyEditor/components/mobile/Divider/index.vue new file mode 100644 index 00000000..f7785043 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/Divider/index.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/Divider/property.vue b/src/components/DiyEditor/components/mobile/Divider/property.vue new file mode 100644 index 00000000..3d7be26d --- /dev/null +++ b/src/components/DiyEditor/components/mobile/Divider/property.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/NavigationBar/config.ts b/src/components/DiyEditor/components/mobile/NavigationBar/config.ts new file mode 100644 index 00000000..b250f5f1 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/NavigationBar/config.ts @@ -0,0 +1,38 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 顶部导航栏属性 */ +export interface NavigationBarProperty { + // 页面标题 + title: string + // 页面描述 + description: string + // 顶部导航高度 + navBarHeight: number + // 页面背景颜色 + backgroundColor: string + // 页面背景图片 + backgroundImage: string + // 样式类型:默认 | 沉浸式 + styleType: 'default' | 'immersion' + // 常驻显示 + alwaysShow: boolean + // 是否显示返回按钮 + showGoBack: boolean +} + +// 定义组件 +export const component = { + id: 'NavigationBar', + name: '顶部导航栏', + icon: 'tabler:layout-navbar', + property: { + title: '页面标题', + description: '', + navBarHeight: 35, + backgroundColor: '#f5f5f5', + backgroundImage: '', + styleType: 'default', + alwaysShow: true, + showGoBack: true + } +} as DiyComponent diff --git a/src/components/DiyEditor/components/mobile/NavigationBar/index.vue b/src/components/DiyEditor/components/mobile/NavigationBar/index.vue new file mode 100644 index 00000000..953748c7 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/NavigationBar/index.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/components/DiyEditor/components/mobile/NavigationBar/property.vue b/src/components/DiyEditor/components/mobile/NavigationBar/property.vue new file mode 100644 index 00000000..c4ca4588 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/NavigationBar/property.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/NoticeBar/config.ts b/src/components/DiyEditor/components/mobile/NoticeBar/config.ts new file mode 100644 index 00000000..03e7143a --- /dev/null +++ b/src/components/DiyEditor/components/mobile/NoticeBar/config.ts @@ -0,0 +1,39 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 公告栏属性 */ +export interface NoticeBarProperty { + // 图标地址 + iconUrl: string + // 公告内容列表 + contents: NoticeContentProperty[] + // 背景颜色 + backgroundColor: string + // 文字颜色 + textColor: string +} + +/** 内容属性 */ +export interface NoticeContentProperty { + // 内容文字 + text: string + // 链接地址 + url: string +} + +// 定义组件 +export const component = { + id: 'NoticeBar', + name: '公告栏', + icon: 'ep:bell', + property: { + iconUrl: 'http://mall.yudao.iocoder.cn/static/images/xinjian.png', + contents: [ + { + text: '', + url: '' + } + ], + backgroundColor: '#fff', + textColor: '#333' + } +} as DiyComponent diff --git a/src/components/DiyEditor/components/mobile/NoticeBar/index.vue b/src/components/DiyEditor/components/mobile/NoticeBar/index.vue new file mode 100644 index 00000000..dc360688 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/NoticeBar/index.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/NoticeBar/property.vue b/src/components/DiyEditor/components/mobile/NoticeBar/property.vue new file mode 100644 index 00000000..11e7f4b7 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/NoticeBar/property.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/PageConfig/config.ts b/src/components/DiyEditor/components/mobile/PageConfig/config.ts new file mode 100644 index 00000000..f8e45e45 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/PageConfig/config.ts @@ -0,0 +1,23 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 页面设置属性 */ +export interface PageConfigProperty { + // 页面描述 + description: string + // 页面背景颜色 + backgroundColor: string + // 页面背景图片 + backgroundImage: string +} + +// 定义页面组件 +export const component = { + id: 'PageConfig', + name: '页面设置', + icon: 'ep:document', + property: { + description: '', + backgroundColor: '#f5f5f5', + backgroundImage: '' + } +} as DiyComponent diff --git a/src/components/DiyEditor/components/mobile/PageConfig/property.vue b/src/components/DiyEditor/components/mobile/PageConfig/property.vue new file mode 100644 index 00000000..278bc940 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/PageConfig/property.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/SearchBar/config.ts b/src/components/DiyEditor/components/mobile/SearchBar/config.ts new file mode 100644 index 00000000..1241748d --- /dev/null +++ b/src/components/DiyEditor/components/mobile/SearchBar/config.ts @@ -0,0 +1,35 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 搜索框属性 */ +export interface SearchProperty { + height: number // 搜索栏高度 + showScan: boolean // 显示扫一扫 + borderRadius: number // 框体样式 + placeholder: string // 占位文字 + placeholderPosition: PlaceholderPosition // 占位文字位置 + backgroundColor: string // 背景颜色 + borderColor: string // 框体颜色 + textColor: string // 字体颜色 + hotKeywords: string[] // 热词 +} + +// 文字位置 +export type PlaceholderPosition = 'left' | 'center' + +// 定义组件 +export const component = { + id: 'SearchBar', + name: '搜索框', + icon: 'ep:search', + property: { + height: 28, + showScan: false, + borderRadius: 0, + placeholder: '搜索商品', + placeholderPosition: 'left', + backgroundColor: 'rgb(249, 249, 249)', + borderColor: 'rgb(255, 255, 255)', + textColor: 'rgb(150, 151, 153)', + hotKeywords: [] + } +} as DiyComponent diff --git a/src/components/DiyEditor/components/mobile/SearchBar/index.vue b/src/components/DiyEditor/components/mobile/SearchBar/index.vue new file mode 100644 index 00000000..e120405a --- /dev/null +++ b/src/components/DiyEditor/components/mobile/SearchBar/index.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/SearchBar/property.vue b/src/components/DiyEditor/components/mobile/SearchBar/property.vue new file mode 100644 index 00000000..9123ebe5 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/SearchBar/property.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/TabBar/config.ts b/src/components/DiyEditor/components/mobile/TabBar/config.ts new file mode 100644 index 00000000..4a570a8e --- /dev/null +++ b/src/components/DiyEditor/components/mobile/TabBar/config.ts @@ -0,0 +1,91 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 底部导航菜单属性 */ +export interface TabBarProperty { + // 选项列表 + items: TabBarItemProperty[] + // 主题 + theme: string + // 样式 + style: TabBarStyle +} + +// 选项属性 +export interface TabBarItemProperty { + name: string // 标签名称 + link: string // 链接 + iconUrl: string // 默认图标链接 + activeIconUrl: string // 选中的图标链接 +} + +// 样式 +export interface TabBarStyle { + // 背景类型 + backgroundType: 'color' | 'img' + // 背景颜色 或 图片链接 + background: string + // 默认颜色 + color: string + // 选中的颜色 + activeColor: string +} + +// 定义组件 +export const component = { + id: 'TabBar', + name: '底部导航', + icon: 'fluent:table-bottom-row-16-filled', + property: { + theme: 'red', + style: { + backgroundType: 'color', + background: '#fff', + color: '#282828', + activeColor: '#fc4141' + }, + items: [ + { + name: '首页', + link: '/', + iconUrl: 'http://mall.yudao.iocoder.cn/static/images/1-001.png', + activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/1-002.png' + }, + { + name: '分类', + link: '/pages/goods_cate/goods_cate', + iconUrl: 'http://mall.yudao.iocoder.cn/static/images/2-001.png', + activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/2-002.png' + }, + { + name: '购物车', + link: '/pages/order_addcart/order_addcart', + iconUrl: 'http://mall.yudao.iocoder.cn/static/images/3-001.png', + activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/3-002.png' + }, + { + name: '我的', + link: '/pages/user/index', + iconUrl: 'http://mall.yudao.iocoder.cn/static/images/4-001.png', + activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/4-002.png' + } + ] + } +} as DiyComponent + +export const THEME_LIST = [ + { id: 'red', name: '中国红', icon: 'icon-park-twotone:theme', color: '#d10019' }, + { id: 'orange', name: '桔橙', icon: 'icon-park-twotone:theme', color: '#f37b1d' }, + { id: 'gold', name: '明黄', icon: 'icon-park-twotone:theme', color: '#fbbd08' }, + { id: 'green', name: '橄榄绿', icon: 'icon-park-twotone:theme', color: '#8dc63f' }, + { id: 'cyan', name: '天青', icon: 'icon-park-twotone:theme', color: '#1cbbb4' }, + { id: 'blue', name: '海蓝', icon: 'icon-park-twotone:theme', color: '#0081ff' }, + { id: 'purple', name: '姹紫', icon: 'icon-park-twotone:theme', color: '#6739b6' }, + { id: 'brightRed', name: '嫣红', icon: 'icon-park-twotone:theme', color: '#e54d42' }, + { id: 'forestGreen', name: '森绿', icon: 'icon-park-twotone:theme', color: '#39b54a' }, + { id: 'mauve', name: '木槿', icon: 'icon-park-twotone:theme', color: '#9c26b0' }, + { id: 'pink', name: '桃粉', icon: 'icon-park-twotone:theme', color: '#e03997' }, + { id: 'brown', name: '棕褐', icon: 'icon-park-twotone:theme', color: '#a5673f' }, + { id: 'grey', name: '玄灰', icon: 'icon-park-twotone:theme', color: '#8799a3' }, + { id: 'gray', name: '草灰', icon: 'icon-park-twotone:theme', color: '#aaaaaa' }, + { id: 'black', name: '墨黑', icon: 'icon-park-twotone:theme', color: '#333333' } +] diff --git a/src/components/DiyEditor/components/mobile/TabBar/index.vue b/src/components/DiyEditor/components/mobile/TabBar/index.vue new file mode 100644 index 00000000..266a20c5 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/TabBar/index.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/components/DiyEditor/components/mobile/TabBar/property.vue b/src/components/DiyEditor/components/mobile/TabBar/property.vue new file mode 100644 index 00000000..512bb5a8 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/TabBar/property.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/src/components/DiyEditor/components/mobile/TitleBar/config.ts b/src/components/DiyEditor/components/mobile/TitleBar/config.ts new file mode 100644 index 00000000..3d486cc3 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/TitleBar/config.ts @@ -0,0 +1,65 @@ +import { DiyComponent } from '@/components/DiyEditor/util' + +/** 标题栏属性 */ +export interface TitleBarProperty { + // 主标题 + title: string + // 副标题 + description: string + // 标题大小 + titleSize: number + // 描述大小 + descriptionSize: number + // 标题粗细 + titleWeight: number + // 显示位置 + position: 'left' | 'center' + // 描述粗细 + descriptionWeight: number + // 标题颜色 + titleColor: string + // 描述颜色 + descriptionColor: string + // 背景颜色 + backgroundColor: string + // 底部分割线 + showBottomBorder: false + // 查看更多 + more: { + // 是否显示查看更多 + show: false + // 样式选择 + type: 'text' | 'icon' | 'all' + // 自定义文字 + text: string + // 链接 + url: string + } +} + +// 定义组件 +export const component = { + id: 'TitleBar', + name: '标题栏', + icon: 'material-symbols:line-start', + property: { + title: '主标题', + description: '副标题', + titleSize: 16, + descriptionSize: 12, + titleWeight: 400, + position: 'left', + descriptionWeight: 200, + titleColor: 'rgba(50, 50, 51, 10)', + descriptionColor: 'rgba(150, 151, 153, 10)', + backgroundColor: 'rgba(255, 255, 255, 10)', + showBottomBorder: false, + more: { + //查看更多 + show: false, + type: 'icon', + text: '查看更多', + url: '' + } + } +} as DiyComponent diff --git a/src/components/DiyEditor/components/mobile/TitleBar/index.vue b/src/components/DiyEditor/components/mobile/TitleBar/index.vue new file mode 100644 index 00000000..aab65779 --- /dev/null +++ b/src/components/DiyEditor/components/mobile/TitleBar/index.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/components/DiyEditor/components/mobile/TitleBar/property.vue b/src/components/DiyEditor/components/mobile/TitleBar/property.vue new file mode 100644 index 00000000..3e4dac2d --- /dev/null +++ b/src/components/DiyEditor/components/mobile/TitleBar/property.vue @@ -0,0 +1,115 @@ + + + + diff --git a/src/components/DiyEditor/components/mobile/index.ts b/src/components/DiyEditor/components/mobile/index.ts new file mode 100644 index 00000000..c0dc67da --- /dev/null +++ b/src/components/DiyEditor/components/mobile/index.ts @@ -0,0 +1,61 @@ +/* + * 组件注册 + * + * 组件规范: + * 1. 每个子目录就是一个独立的组件,每个目录包括以下三个文件: + * 2. config.ts:组件配置,必选,用于定义组件、组件默认的属性、定义属性的类型 + * 3. index.vue:组件展示,用于展示组件的渲染效果。可以不提供,如 Page(页面设置),只需要属性配置表单即可 + * 4. property.vue:组件属性表单,用于配置组件,必选, + * + * 注: + * 组件ID以config.ts中配置的id为准,与组件目录的名称无关,但还是建议组件目录的名称与组件ID保持一致 + */ + +// 导入组件界面模块 +const viewModules: Record = import.meta.glob('./*/*.vue') +// 导入配置模块 +const configModules: Record = import.meta.glob('./*/config.ts', { eager: true }) + +// 界面模块 +const components = {} +// 组件配置模块 +const componentConfigs = {} + +// 组件界面的类型 +type ViewType = 'index' | 'property' + +/** + * 注册组件的界面模块 + * + * @param componentId 组件ID + * @param configPath 配置模块的文件路径 + * @param viewType 组件界面的类型 + */ +const registerComponentViewModule = ( + componentId: string, + configPath: string, + viewType: ViewType +) => { + const viewPath = configPath.replace('config.ts', `${viewType}.vue`) + const viewModule = viewModules[viewPath] + if (viewModule) { + // 定义异步组件 + components[componentId] = defineAsyncComponent(viewModule) + } +} + +// 注册 +Object.keys(configModules).forEach((modulePath: string) => { + const component = configModules[modulePath].component + const componentId = component?.id + if (componentId) { + // 注册组件 + componentConfigs[componentId] = component + // 注册预览界面 + registerComponentViewModule(componentId, modulePath, 'index') + // 注册属性配置表单 + registerComponentViewModule(`${componentId}Property`, modulePath, 'property') + } +}) + +export { components, componentConfigs } diff --git a/src/components/DiyEditor/index.vue b/src/components/DiyEditor/index.vue new file mode 100644 index 00000000..520cd497 --- /dev/null +++ b/src/components/DiyEditor/index.vue @@ -0,0 +1,539 @@ + + + + diff --git a/src/components/DiyEditor/util.ts b/src/components/DiyEditor/util.ts new file mode 100644 index 00000000..407efa30 --- /dev/null +++ b/src/components/DiyEditor/util.ts @@ -0,0 +1,59 @@ +import { ref, Ref } from 'vue' +import { PageConfigProperty } from '@/components/DiyEditor/components/mobile/PageConfig/config' +import { NavigationBarProperty } from '@/components/DiyEditor/components/mobile/NavigationBar/config' +import { TabBarProperty } from '@/components/DiyEditor/components/mobile/TabBar/config' + +export interface DiyComponent { + id: string + name: string + icon: string + property: T +} + +export interface DiyComponentLibrary { + name: string + extended: boolean + components: string[] +} + +// 页面配置 +export interface PageConfig { + // 页面属性 + page: PageConfigProperty + // 顶部导航栏属性 + navigationBar: NavigationBarProperty + // 底部导航菜单属性 + tabBar: TabBarProperty + // 页面组件列表 + components: PageComponent[] +} +// 页面组件,只保留组件ID,组件属性 +export interface PageComponent extends Pick, 'id' | 'property'> {} + +// 属性表单监听 +export function usePropertyForm(modelValue: T, emit: Function): { formData: Ref } { + const formData = ref() + // 监听属性数据变动 + watch( + () => modelValue, + () => { + formData.value = modelValue + }, + { + deep: true, + immediate: true + } + ) + // 监听表单数据变动 + watch( + () => formData.value, + () => { + emit('update:modelValue', formData.value) + }, + { + deep: true + } + ) + + return { formData } +} diff --git a/src/components/UploadFile/src/UploadImg.vue b/src/components/UploadFile/src/UploadImg.vue index 3cfc0a73..996fe40b 100644 --- a/src/components/UploadFile/src/UploadImg.vue +++ b/src/components/UploadFile/src/UploadImg.vue @@ -18,15 +18,15 @@
- {{ t('action.edit') }} + {{ t('action.edit') }}
- {{ t('action.detail') }} + {{ t('action.detail') }}
-
+
- {{ t('action.del') }} + {{ t('action.del') }}
@@ -81,7 +81,11 @@ const props = defineProps({ fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"]) height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px) width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px) - borderradius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传(默认为 8px) + borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px) + // 是否显示删除按钮 + showDelete: propTypes.bool.def(true), + // 是否显示按钮文字 + showBtnText: propTypes.bool.def(true) }) const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index aa260cf1..70dc5b2f 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -459,6 +459,34 @@ const remainingRouter: AppRouteRecordRaw[] = [ component: () => import('@/views/pay/cashier/index.vue') } ] + }, + { + path: '/diy', + name: 'DiyCenter', + meta: { hidden: true }, + component: Layout, + children: [ + { + path: 'template/decorate/:id', + name: 'DiyTemplateDecorate', + meta: { + title: '模板装修', + noCache: true, + hidden: true + }, + component: () => import('@/views/mall/promotion/diy/template/decorate.vue') + }, + { + path: 'page/decorate/:id', + name: 'DiyPageDecorate', + meta: { + title: '页面装修', + noCache: true, + hidden: true + }, + component: () => import('@/views/mall/promotion/diy/page/decorate.vue') + } + ] } ] diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 12d01507..5a818fa9 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -190,5 +190,8 @@ export enum DICT_TYPE { // ========== CRM - 客户管理模块 ========== CRM_RECEIVABLE_CHECK_STATUS = 'crm_receivable_check_status', - CRM_RETURN_TYPE = 'crm_return_type' + CRM_RETURN_TYPE = 'crm_return_type', + CRM_CUSTOMER_INDUSTRY = 'crm_customer_industry', + CRM_CUSTOMER_LEVEL = 'crm_customer_level', + CRM_CUSTOMER_SOURCE = 'crm_customer_source' } diff --git a/src/views/Login/components/MobileForm.vue b/src/views/Login/components/MobileForm.vue index ff9069f4..7f5d9942 100644 --- a/src/views/Login/components/MobileForm.vue +++ b/src/views/Login/components/MobileForm.vue @@ -102,6 +102,7 @@ import { usePermissionStore } from '@/store/modules/permission' import { getTenantIdByName, sendSmsCode, smsLogin } from '@/api/login' import LoginFormTitle from './LoginFormTitle.vue' import { LoginStateEnum, useFormValid, useLoginState } from './useLogin' +import { ElLoading } from 'element-plus' defineOptions({ name: 'MobileForm' }) diff --git a/src/views/crm/clue/ClueForm.vue b/src/views/crm/clue/ClueForm.vue index e72e4228..877b26d9 100644 --- a/src/views/crm/clue/ClueForm.vue +++ b/src/views/crm/clue/ClueForm.vue @@ -7,28 +7,6 @@ label-width="100px" v-loading="formLoading" > - - - - {{ dict.label }} - - - - - - - {{ dict.label }} - - - @@ -80,8 +58,6 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ id: undefined, - transformStatus: undefined, - followUpStatus: undefined, name: undefined, customerId: undefined, contactNextTime: undefined, @@ -93,10 +69,8 @@ const formData = ref({ remark: undefined }) const formRules = reactive({ - transformStatus: [{ required: true, message: '转化状态不能为空', trigger: 'blur' }], - followUpStatus: [{ required: true, message: '跟进状态不能为空', trigger: 'blur' }], name: [{ required: true, message: '线索名称不能为空', trigger: 'blur' }], - customerId: [{ required: true, message: '客户id不能为空', trigger: 'blur' }] + customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }] }) const formRef = ref() // 表单 Ref @@ -148,8 +122,6 @@ const submitForm = async () => { const resetForm = () => { formData.value = { id: undefined, - transformStatus: undefined, - followUpStatus: undefined, name: undefined, customerId: undefined, contactNextTime: undefined, diff --git a/src/views/crm/clue/index.vue b/src/views/crm/clue/index.vue index 42f20545..e866a725 100644 --- a/src/views/crm/clue/index.vue +++ b/src/views/crm/clue/index.vue @@ -8,36 +8,6 @@ :inline="true" label-width="68px" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - 搜索 重置 @@ -238,17 +148,9 @@ const list = ref([]) // 列表的数据 const queryParams = reactive({ pageNo: 1, pageSize: 10, - transformStatus: null, - followUpStatus: null, name: null, - customerId: null, - contactNextTime: [], telephone: null, - mobile: null, - address: null, - ownerUserId: null, - contactLastTime: [], - createTime: [] + mobile: null }) const queryFormRef = ref() // 搜索的表单 const exportLoading = ref(false) // 导出的加载中 diff --git a/src/views/crm/contract/index.vue b/src/views/crm/contract/index.vue index 7a8211be..1670c418 100644 --- a/src/views/crm/contract/index.vue +++ b/src/views/crm/contract/index.vue @@ -166,7 +166,7 @@ import download from '@/utils/download' import * as ContractApi from '@/api/crm/contract' import ContractForm from './ContractForm.vue' -defineOptions({ name: 'Contract' }) +defineOptions({ name: 'CrmContract' }) const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 diff --git a/src/views/crm/customer/CustomerForm.vue b/src/views/crm/customer/CustomerForm.vue new file mode 100644 index 00000000..6e5cd62e --- /dev/null +++ b/src/views/crm/customer/CustomerForm.vue @@ -0,0 +1,205 @@ + + diff --git a/src/views/crm/customer/index.vue b/src/views/crm/customer/index.vue new file mode 100644 index 00000000..ace56dac --- /dev/null +++ b/src/views/crm/customer/index.vue @@ -0,0 +1,228 @@ + + + diff --git a/src/views/mall/promotion/banner/BannerForm.vue b/src/views/mall/promotion/banner/BannerForm.vue index 687f26a8..03bca0f9 100644 --- a/src/views/mall/promotion/banner/BannerForm.vue +++ b/src/views/mall/promotion/banner/BannerForm.vue @@ -9,12 +9,12 @@ > - - + + - + @@ -42,7 +42,7 @@ - + + + + + + + + + + + + + + + + + diff --git a/src/views/mall/promotion/diy/page/decorate.vue b/src/views/mall/promotion/diy/page/decorate.vue new file mode 100644 index 00000000..76336bc4 --- /dev/null +++ b/src/views/mall/promotion/diy/page/decorate.vue @@ -0,0 +1,99 @@ + + diff --git a/src/views/mall/promotion/diy/page/index.vue b/src/views/mall/promotion/diy/page/index.vue new file mode 100644 index 00000000..6436c2f7 --- /dev/null +++ b/src/views/mall/promotion/diy/page/index.vue @@ -0,0 +1,189 @@ + + + diff --git a/src/views/mall/promotion/diy/template/DiyTemplateForm.vue b/src/views/mall/promotion/diy/template/DiyTemplateForm.vue new file mode 100644 index 00000000..e4289f65 --- /dev/null +++ b/src/views/mall/promotion/diy/template/DiyTemplateForm.vue @@ -0,0 +1,115 @@ + + diff --git a/src/views/mall/promotion/diy/template/decorate.vue b/src/views/mall/promotion/diy/template/decorate.vue new file mode 100644 index 00000000..9c7500b2 --- /dev/null +++ b/src/views/mall/promotion/diy/template/decorate.vue @@ -0,0 +1,134 @@ + + diff --git a/src/views/mall/promotion/diy/template/index.vue b/src/views/mall/promotion/diy/template/index.vue new file mode 100644 index 00000000..dcf13698 --- /dev/null +++ b/src/views/mall/promotion/diy/template/index.vue @@ -0,0 +1,225 @@ + + + diff --git a/src/views/mall/trade/order/detail/index.vue b/src/views/mall/trade/order/detail/index.vue index 38b9342e..67e54767 100644 --- a/src/views/mall/trade/order/detail/index.vue +++ b/src/views/mall/trade/order/detail/index.vue @@ -16,7 +16,7 @@ - + {{ formData.brokerageUser?.nickname }} @@ -26,7 +26,7 @@ - + {{ formData.receiverMobile }}
- + {{ formData.receiverAreaName }} {{ formData.receiverDetailAddress }} - + {{ deliveryExpressList.find((item) => item.id === formData.logisticsId)?.name }} - + {{ formData.logisticsNo }} - + {{ formatDate(formData.deliveryTime) }} - +
- + {{ pickUpStore?.name }}
diff --git a/src/views/mall/trade/order/index.vue b/src/views/mall/trade/order/index.vue index e08715df..c892292a 100644 --- a/src/views/mall/trade/order/index.vue +++ b/src/views/mall/trade/order/index.vue @@ -125,10 +125,10 @@