feat(shop): 新增商城基础设置组件
- 新增商城基础信息配置界面支持店铺名称、Logo、描述、电话、地址和开关配置 - 实现图片选择和删除功能,支持Logo的上传回显 - 集成表单校验和保存接口调用,提供保存状态反馈 - 优化响应式布局适配不同屏幕尺寸 fix(cms): 防止文章编辑内容的XSS攻击 - 在文章编辑组件中对动态HTML内容添加DOMPurify消毒 - 替换 v-html 渲染为安全消毒后的内容展现 - 确保富文本内容安全,防止跨站脚本漏洞 refactor(system-setting): 优化系统设置基本信息组件逻辑 - 替换ico文件上传组件,改用SelectFile实现图片选择和删除功能 - 简化图标上传流程,移除上传接口调用相关代码 - 统一表单数据处理,增强设置数据解析和回显兼容性 - 调整保存逻辑,支持根据是否存在主键调用新增或更新接口 - 改进watch数据响应逻辑,支持多种数据结构兼容 fix(system-setting): 修正清理设置组件数据重置逻辑 - 统一清理设置组件的 settingKey 值为 clear,避免混淆 - 优化数据监听回调,支持不同数据结构和空数据重置表单 - 确保组件初始化状态正确,避免遗留数据影响展示 fix(store): 修正 chat store 定义方式 - 按 pinia 官方规范简化 store 定义参数 - 修复 store id 错误传递问题,确保正确注册和使用
This commit is contained in:
157
vite.config.ts
157
vite.config.ts
@@ -4,30 +4,83 @@ import ViteCompression from 'vite-plugin-compression';
|
||||
import ViteComponents from 'unplugin-vue-components/vite';
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
|
||||
import { EleAdminResolver } from 'ele-admin-pro/lib/utils/resolvers';
|
||||
import { DynamicAntdLess } from 'ele-admin-pro/lib/utils/dynamic-theme';
|
||||
import { resolve } from 'path';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { splitVendorChunkPlugin } from 'vite';
|
||||
|
||||
/**
|
||||
* AntDv4 兼容:替换 ele-admin-pro Less 中的 colorPalette JS 调用
|
||||
* AntDv4 移除了 Less 的 colorPalette 函数,ele-admin-pro 的 common.less
|
||||
* 中有 ~`colorPalette('@{primary-color}', 5)` 等 JS 表达式,
|
||||
* 此插件在 Less 编译前将其替换为硬编码的颜色值。
|
||||
*/
|
||||
function colorPaletteReplacer() {
|
||||
return {
|
||||
name: 'color-palette-replacer',
|
||||
enforce: 'pre',
|
||||
async transform(code, id) {
|
||||
if (!id.includes('ele-admin-pro') || !id.endsWith('.less')) {
|
||||
return null;
|
||||
}
|
||||
console.log('🎨 Processing Less file:', id);
|
||||
let result = code;
|
||||
result = result.replace(/~`colorPalette\('@\{primary-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
|
||||
const colors = { 1: '#e6f4ff', 2: '#bae0ff', 3: '#91caff', 4: '#69b1ff', 5: '#4096ff', 6: '#1677ff', 7: '#0958d9', 8: '#003eb3', 9: '#002c8c', 10: '#001d66' };
|
||||
return colors[idx] || '#1677ff';
|
||||
});
|
||||
result = result.replace(/~`colorPalette\('@\{success-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
|
||||
const colors = { 1: '#f6ffed', 2: '#d9f7be', 3: '#b7eb8f', 4: '#95de64', 5: '#73d13d', 6: '#52c41a', 7: '#389e0d', 8: '#237804', 9: '#135200', 10: '#092b00' };
|
||||
return colors[idx] || '#52c41a';
|
||||
});
|
||||
result = result.replace(/~`colorPalette\('@\{warning-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
|
||||
const colors = { 1: '#fffbe6', 2: '#fff1b8', 3: '#ffe58f', 4: '#ffd666', 5: '#ffc53d', 6: '#faad14', 7: '#d48806', 8: '#ad6800', 9: '#874d00', 10: '#612500' };
|
||||
return colors[idx] || '#faad14';
|
||||
});
|
||||
result = result.replace(/~`colorPalette\('@\{error-color\}',\s*(\d+)\)\s*`/g, (_match, idx) => {
|
||||
const colors = { 1: '#fff2f0', 2: '#ffccc7', 3: '#ffa39e', 4: '#ff7875', 5: '#ff4d4f', 6: '#f5222d', 7: '#cf1322', 8: '#a8071a', 9: '#820014', 10: '#5c0011' };
|
||||
return colors[idx] || '#ff4d4f';
|
||||
});
|
||||
if (result !== code) {
|
||||
console.log('✅ Replaced colorPalette in:', id);
|
||||
}
|
||||
return result !== code ? result : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* AntDv4 兼容:将 ele-admin-pro 引用的 AntDv3 样式模块重定向到空模块
|
||||
* AntDv4 使用 CSS-in-JS,不再有 es/xxx/style 目录
|
||||
*/
|
||||
function antdStyleRedirect() {
|
||||
const emptyModule = resolve('src/styles/antd-less-stub/empty-style.js');
|
||||
return {
|
||||
name: 'antd-style-redirect',
|
||||
enforce: 'pre',
|
||||
resolveId(source) {
|
||||
if (source.startsWith('ant-design-vue/es/') && source.endsWith('/style')) {
|
||||
return emptyModule;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 简化的智能端口管理(避免构建时模块解析问题)
|
||||
function getSmartPort() {
|
||||
try {
|
||||
// 从环境变量获取基础配置
|
||||
const basePort = parseInt(process.env.VITE_BASE_PORT || '3000');
|
||||
const tenantId = process.env.VITE_TENANT_ID || '10258';
|
||||
const environment = process.env.NODE_ENV || 'development';
|
||||
|
||||
// 简化的端口计算
|
||||
let recommendedPort = basePort;
|
||||
|
||||
if (environment === 'development') {
|
||||
// 开发环境:基础端口 + 租户偏移
|
||||
const tenantOffset = (parseInt(tenantId) % 1000) * 10;
|
||||
recommendedPort = basePort + tenantOffset;
|
||||
} else if (environment === 'test') {
|
||||
recommendedPort = basePort + 1000;
|
||||
} else if (environment === 'production') {
|
||||
recommendedPort = 8080; // 生产环境使用标准端口
|
||||
recommendedPort = 8080;
|
||||
}
|
||||
|
||||
console.log('🎯 智能端口计算:', {
|
||||
@@ -46,26 +99,23 @@ function getSmartPort() {
|
||||
|
||||
export default defineConfig(({ command }) => {
|
||||
const isBuild = command === 'build';
|
||||
|
||||
// 智能端口配置(仅在开发模式下)
|
||||
const smartPort = !isBuild ? getSmartPort() : undefined;
|
||||
|
||||
return {
|
||||
// 在这里增加 base 写子路径
|
||||
base: '/',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@/': resolve('src') + '/',
|
||||
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
|
||||
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
|
||||
// AntDv4 兼容层:将 ele-admin-pro 引用的 AntD Less 主题文件重定向到 stub
|
||||
'ant-design-vue/es/style/themes/default.less': resolve('src/styles/antd-less-stub/default.less'),
|
||||
'ant-design-vue/es/style/themes/dark.less': resolve('src/styles/antd-less-stub/dark.less')
|
||||
}
|
||||
},
|
||||
// 智能服务器配置
|
||||
server: {
|
||||
port: smartPort || 3000,
|
||||
host: '0.0.0.0', // 允许外部访问
|
||||
open: true, // 自动打开浏览器
|
||||
cors: true, // 启用 CORS
|
||||
// 代理配置
|
||||
host: '0.0.0.0',
|
||||
open: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: process.env.VITE_API_URL || 'https://server.websoft.top',
|
||||
@@ -84,30 +134,28 @@ export default defineConfig(({ command }) => {
|
||||
},
|
||||
}
|
||||
},
|
||||
// 端口冲突时的处理
|
||||
strictPort: false, // 允许自动选择其他端口
|
||||
strictPort: false,
|
||||
},
|
||||
// 预览服务器配置(用于生产构建预览)
|
||||
preview: {
|
||||
port: smartPort ? smartPort + 1000 : 4173,
|
||||
host: '0.0.0.0',
|
||||
open: true,
|
||||
cors: true,
|
||||
strictPort: false,
|
||||
},
|
||||
plugins: [
|
||||
colorPaletteReplacer(),
|
||||
antdStyleRedirect(),
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
}),
|
||||
// 组件按需引入
|
||||
ViteComponents({
|
||||
dts: false,
|
||||
resolvers: [
|
||||
AntDesignVueResolver({
|
||||
importStyle: isBuild ? 'less' : false
|
||||
importStyle: false // AntDv4 使用 CSS-in-JS,无需 Less 导入
|
||||
}),
|
||||
EleAdminResolver({
|
||||
importStyle: isBuild ? 'less' : false
|
||||
@@ -115,23 +163,18 @@ export default defineConfig(({ command }) => {
|
||||
],
|
||||
directoryAsNamespace: true
|
||||
}),
|
||||
// 代码分割
|
||||
splitVendorChunkPlugin(),
|
||||
// gzip 压缩
|
||||
ViteCompression({
|
||||
disable: !isBuild,
|
||||
threshold: 10240,
|
||||
algorithm: 'gzip',
|
||||
ext: '.gz'
|
||||
}),
|
||||
// brotli 压缩
|
||||
ViteCompression({
|
||||
disable: !isBuild,
|
||||
threshold: 10240,
|
||||
algorithm: 'brotliCompress',
|
||||
ext: '.br'
|
||||
}),
|
||||
// 打包分析
|
||||
isBuild && visualizer({
|
||||
filename: 'dist/stats.html',
|
||||
open: false,
|
||||
@@ -143,9 +186,7 @@ export default defineConfig(({ command }) => {
|
||||
preprocessorOptions: {
|
||||
less: {
|
||||
javascriptEnabled: true,
|
||||
plugins: [new DynamicAntdLess()],
|
||||
modifyVars: {
|
||||
// 组件样式开发环境全局引入生产环境按需引入
|
||||
'style-entry-file': isBuild ? 'as-needed' : 'global-import'
|
||||
}
|
||||
}
|
||||
@@ -169,7 +210,7 @@ export default defineConfig(({ command }) => {
|
||||
'qrcode',
|
||||
'nprogress'
|
||||
],
|
||||
exclude: ['@iconify/json']
|
||||
exclude: ['@iconify/json', 'tinymce']
|
||||
},
|
||||
build: {
|
||||
target: 'es2015',
|
||||
@@ -177,45 +218,49 @@ export default defineConfig(({ command }) => {
|
||||
chunkSizeWarningLimit: 1000,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// 手动分包
|
||||
manualChunks: {
|
||||
// Vue 生态
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia'],
|
||||
// UI 组件库
|
||||
'ui-vendor': ['ant-design-vue', 'ele-admin-pro'],
|
||||
// 工具库
|
||||
'utils-vendor': ['lodash-es', 'dayjs', 'crypto-js', 'js-md5'],
|
||||
// 图表库
|
||||
'charts-vendor': ['echarts', 'vue-echarts', 'echarts-wordcloud'],
|
||||
// 编辑器
|
||||
'editor-vendor': ['tinymce', 'bytemd', 'md-editor-v3'],
|
||||
// 文件处理
|
||||
'file-vendor': ['xlsx', 'exceljs', 'file-saver', 'ali-oss']
|
||||
manualChunks(id) {
|
||||
if (!id.includes('node_modules')) return undefined;
|
||||
if (id.includes('/vue-router/') || id.includes('/pinia/') || id.includes('/vue/')) {
|
||||
return 'vue-vendor';
|
||||
}
|
||||
if (id.includes('/ant-design-vue/') || id.includes('/ele-admin-pro/')) {
|
||||
return 'ui-vendor';
|
||||
}
|
||||
if (id.includes('/lodash-es/') || id.includes('/dayjs/') || id.includes('/crypto-js/') || id.includes('/js-md5/')) {
|
||||
return 'utils-vendor';
|
||||
}
|
||||
if (id.includes('/echarts/') || id.includes('/vue-echarts/') || id.includes('/echarts-wordcloud/')) {
|
||||
return 'charts-vendor';
|
||||
}
|
||||
if (id.includes('/tinymce/') || id.includes('/bytemd/') || id.includes('/md-editor-v3/')) {
|
||||
return 'editor-vendor';
|
||||
}
|
||||
if (id.includes('/xlsx/') || id.includes('/exceljs/') || id.includes('/file-saver/') || id.includes('/ali-oss/')) {
|
||||
return 'file-vendor';
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
// 文件命名
|
||||
chunkFileNames: (chunkInfo) => {
|
||||
const facadeModuleId = chunkInfo.facadeModuleId
|
||||
const name = chunkInfo.facadeModuleId
|
||||
? chunkInfo.facadeModuleId.split('/').pop().replace(/\.\w+$/, '')
|
||||
: 'chunk';
|
||||
return `js/${facadeModuleId}-[hash].js`;
|
||||
return `js/${name}-[hash].js`;
|
||||
},
|
||||
assetFileNames: (assetInfo) => {
|
||||
const info = assetInfo.name.split('.');
|
||||
const ext = info[info.length - 1];
|
||||
if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name)) {
|
||||
return `media/[name]-[hash].${ext}`;
|
||||
const name = (assetInfo.name || '').split('/').pop();
|
||||
if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(name)) {
|
||||
return `media/[hash].[ext]`;
|
||||
}
|
||||
if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(assetInfo.name)) {
|
||||
return `images/[name]-[hash].${ext}`;
|
||||
if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(name)) {
|
||||
return `images/[hash].[ext]`;
|
||||
}
|
||||
if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) {
|
||||
return `fonts/[name]-[hash].${ext}`;
|
||||
if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(name)) {
|
||||
return `fonts/[hash].[ext]`;
|
||||
}
|
||||
return `assets/[name]-[hash].${ext}`;
|
||||
return `assets/[hash].[ext]`;
|
||||
}
|
||||
}
|
||||
},
|
||||
// 压缩配置
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
|
||||
Reference in New Issue
Block a user