签入版本

This commit is contained in:
2026-09-12 18:57:19 +08:00
parent d45b61d808
commit 2857e0f2da
295 changed files with 20713 additions and 13125 deletions
+2 -2
View File
@@ -2,10 +2,10 @@
VITE_PORT = 8001
# spa-title
VITE_GLOB_APP_TITLE = Django+ElementPlus版
VITE_GLOB_APP_TITLE = Flask+ElementPlus版
# spa shortname
VITE_GLOB_APP_SHORT_NAME = Django+ElementPlus版
VITE_GLOB_APP_SHORT_NAME = Flask+ElementPlus版
# 生产环境 开启mock
VITE_GLOB_PROD_MOCK = false
+1 -1
View File
@@ -11,7 +11,7 @@ VITE_USE_MOCK = false
VITE_DROP_CONSOLE = true
# 跨域代理,可以配置多个,请注意不要换行
VITE_PROXY=[["/api","http://127.0.0.1:8021/"]]
VITE_PROXY=[["/api","http://127.0.0.1:8021/api/v1"]]
# API 接口地址
VITE_GLOB_API_URL =
+1 -1
View File
@@ -1 +1 @@
### Django+ElementPlus版
### Flask+ElementPlus版
+6
View File
@@ -0,0 +1,6 @@
/**
* The name of the configuration file entered in the production environment
*/
export const GLOB_CONFIG_FILE_NAME = 'app.config.js';
export const OUTPUT_DIR = 'dist';
+12
View File
@@ -0,0 +1,12 @@
/**
* Get the configuration file variable name
* @param env
*/
export const getConfigFileName = (env: Record<string, any>) => {
const safe = (env.VITE_GLOB_APP_SHORT_NAME || '__APP')
.replace(/[^\w]/g, '_') // 非字母数字→_
.replace(/_{2,}/g, '_') // 连续 _ 变单个
.replace(/^_|_$/g, '') // 去掉首尾 _
.toUpperCase();
return `__PRODUCTION_${safe}__CONF__`;
};
+44
View File
@@ -0,0 +1,44 @@
/**
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
*/
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
import fs, { writeFileSync } from 'fs-extra';
import chalk from 'chalk';
import { getRootPath, getEnvConfig } from '../utils';
import { getConfigFileName } from '../getConfigFileName';
import pkg from '../../package.json';
function createConfig(
{
configName,
config,
configFileName = GLOB_CONFIG_FILE_NAME,
}: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} }
) {
try {
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}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '');
fs.mkdirp(getRootPath(OUTPUT_DIR));
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
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));
}
}
export function runBuildConfig() {
const config = getEnvConfig();
const configFileName = getConfigFileName(config);
createConfig({ config, configName: configFileName });
}
+23
View File
@@ -0,0 +1,23 @@
// #!/usr/bin/env node
import { runBuildConfig } from './buildConf';
import chalk from 'chalk';
import pkg from '../../package.json';
export const runBuild = async () => {
try {
const argvList = process.argv.splice(2);
// Generate configuration file
if (!argvList.includes('disabled-config')) {
await runBuildConfig();
}
console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
} catch (error) {
console.log(chalk.red('vite build error:\n' + error));
process.exit(1);
}
};
runBuild();
+71
View File
@@ -0,0 +1,71 @@
import fs from 'fs';
import path from 'path';
import dotenv from 'dotenv';
export function isDevFn(mode: string): boolean {
return mode === 'development';
}
export function isProdFn(mode: string): boolean {
return mode === 'production';
}
/**
* Whether to generate package preview
*/
export function isReportMode(): boolean {
return process.env.REPORT === 'true';
}
// Read all environment variable configuration files to process.env
export function wrapperEnv(envConf: Recordable): ViteEnv {
const ret: any = {};
for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') {
realName = Number(realName);
}
if (envName === 'VITE_PROXY') {
try {
realName = JSON.parse(realName);
} catch (error) {}
}
ret[envName] = realName;
process.env[envName] = realName;
}
return ret;
}
/**
* Get the environment variables starting with the specified prefix
* @param match prefix
* @param confFiles ext
*/
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) {
let envConfig = {};
confFiles.forEach((item) => {
try {
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
envConfig = { ...envConfig, ...env };
} catch (error) {}
});
Object.keys(envConfig).forEach((key) => {
const reg = new RegExp(`^(${match})`);
if (!reg.test(key)) {
Reflect.deleteProperty(envConfig, key);
}
});
return envConfig;
}
/**
* Get user root directory
* @param dir file path
*/
export function getRootPath(...dir: string[]) {
return path.resolve(process.cwd(), ...dir);
}
+35
View File
@@ -0,0 +1,35 @@
/**
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
* https://github.com/anncwb/vite-plugin-compression
*/
import type { Plugin } from 'vite';
import compressPlugin from 'vite-plugin-compression';
export function configCompressPlugin(
compress: 'gzip' | 'brotli' | 'none',
deleteOriginFile = false
): Plugin | Plugin[] {
const compressList = compress.split(',');
const plugins: Plugin[] = [];
if (compressList.includes('gzip')) {
plugins.push(
compressPlugin({
ext: '.gz',
deleteOriginFile,
})
);
}
if (compressList.includes('brotli')) {
plugins.push(
compressPlugin({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile,
})
);
}
return plugins;
}
+40
View File
@@ -0,0 +1,40 @@
/**
* Plugin to minimize and use ejs template syntax in index.html.
* https://github.com/anncwb/vite-plugin-html
*/
import type { PluginOption } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
import pkg from '../../../package.json';
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 getAppConfigSrc = () => {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
};
const htmlPlugin: PluginOption[] = createHtmlPlugin({
minify: isBuild,
inject: {
// Inject data into ejs template
data: {
title: VITE_GLOB_APP_TITLE,
},
// Embed the generated app.config.js file
tags: isBuild
? [
{
tag: 'script',
attrs: {
src: getAppConfigSrc(),
},
},
]
: [],
},
});
return htmlPlugin;
}
+59
View File
@@ -0,0 +1,59 @@
import type { PluginOption } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import VueSetupExtend from 'vite-plugin-vue-setup-extend';
// 自动按需导入,根据自身情况选择
import AutoImport from 'unplugin-auto-import/vite';
// import Components from 'unplugin-vue-components/vite';
// import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
// import Components from 'unplugin-vue-components/vite';
// import ElementPlus from 'unplugin-element-plus/vite';
import { configHtmlPlugin } from './html';
import { configMockPlugin } from './mock';
import { configCompressPlugin } from './compress';
import { configVisualizerPlugin } from './visualizer';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, prodMock) {
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
const vitePlugins: (PluginOption | PluginOption[])[] = [
// have to
vue(),
// have to
vueJsx(),
VueSetupExtend(),
// ElementPlus({
// // 引入的样式的类型,可以是css、sass、less等,
// importStyle: 'sass',
// useSource: true,
// }),
// 按需引入element且自动创建组件声明
AutoImport({
imports: ['vue', 'vue-router', 'pinia'], // 自动导入的依赖库数组
dts: './auto-imports.d.ts', // 自动导入类型定义文件路径
}),
// Components({
// resolvers: [ElementPlusResolver()],
// }),
];
// vite-plugin-html
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
// vite-plugin-mock
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild, prodMock));
// rollup-plugin-visualizer
vitePlugins.push(configVisualizerPlugin());
if (isBuild) {
// rollup-plugin-gzip
vitePlugins.push(
configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE),
);
}
return vitePlugins;
}
+19
View File
@@ -0,0 +1,19 @@
/**
* Mock plugin for development and production.
* https://github.com/anncwb/vite-plugin-mock
*/
import { viteMockServe } from 'vite-plugin-mock';
export function configMockPlugin(isBuild: boolean, prodMock: boolean) {
return viteMockServe({
ignore: /^\_/,
mockPath: 'mock',
localEnabled: !isBuild,
prodEnabled: isBuild && prodMock,
injectCode: `
import { setupProdMockServer } from '../mock/_createProductionServer';
setupProdMockServer();
`,
});
}
+15
View File
@@ -0,0 +1,15 @@
import { PluginOption } from 'vite';
import visualizer from 'rollup-plugin-visualizer';
import { isReportMode } from '../../utils';
export function configVisualizerPlugin() {
if (isReportMode()) {
return visualizer({
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
}) as PluginOption;
}
return [];
}
+34
View File
@@ -0,0 +1,34 @@
/**
* Used to parse the .env.development proxy configuration
*/
import type { ProxyOptions } from 'vite';
type ProxyItem = [string, string];
type ProxyList = ProxyItem[];
type ProxyTargetList = Record<string, ProxyOptions & { rewrite: (path: string) => string }>;
const httpsRE = /^https:\/\//;
/**
* Generate proxy
* @param list
*/
export function createProxy(list: ProxyList = []) {
const ret: ProxyTargetList = {};
for (const [prefix, target] of list) {
const isHttps = httpsRE.test(target);
// https://github.com/http-party/node-http-proxy#options
ret[prefix] = {
target: target,
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
// https is require secure=false
...(isHttps ? { secure: false } : {}),
};
}
return ret;
}
+2 -2
View File
@@ -1,10 +1,10 @@
{
"name": "djangoadmin",
"version": "3.0.0",
"description": "Django+ElementPlus版",
"description": "Flask+ElementPlus版",
"license": "Apache-2.0",
"author": {
"name": "Django+ElementPlus版",
"name": "Flask+ElementPlus版",
"email": "djangoadmin@126.com",
"url": "https://www.djangoadmin.cn"
},
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

+10
View File
@@ -61,3 +61,13 @@ export function getTemplateByCode(code: any) {
});
}
/**
* @description: 发送消息
*/
export function sendMsg(data) {
return http.request({
url: '/websocket/sendMsg',
method: 'post',
data,
});
}
+3 -3
View File
@@ -83,7 +83,7 @@ export function jobBatchDelete(data: any) {
export function getJobRunOnce(id) {
return http.request({
url: '/job/runOnce/' + id,
method: 'get',
method: 'post',
});
}
@@ -104,7 +104,7 @@ export function setJobStatus(data: any) {
export function getJobPause(id) {
return http.request({
url: '/job/pause/' + id,
method: 'get',
method: 'put',
});
}
@@ -114,7 +114,7 @@ export function getJobPause(id) {
export function getJobResume(id) {
return http.request({
url: '/job/resume/' + id,
method: 'get',
method: 'put',
});
}
+12 -12
View File
@@ -1,7 +1,7 @@
import { http } from '@/utils/http/axios';
/**
* @description: 案例分页列表
* @description: 案例演示分页列表
*/
export function getExamplePage(params?) {
return http.request({
@@ -12,7 +12,7 @@ export function getExamplePage(params?) {
}
/**
* 获取全部案例列表
* 获取全部案例演示列表
* @param params 参数
* @returns 返回结果
*/
@@ -25,8 +25,8 @@ export function getExampleList(params?) {
}
/**
* @description: 根据ID获取案例详情
* @param id 案例ID
* @description: 根据ID获取案例演示详情
* @param id 案例演示ID
*/
export function getExampleDetail(id) {
return http.request({
@@ -36,8 +36,8 @@ export function getExampleDetail(id) {
}
/**
* @description: 添加案例
* @param data 案例数据
* @description: 添加案例演示
* @param data 案例演示数据
*/
export function exampleAdd(data: any) {
return http.request({
@@ -48,8 +48,8 @@ export function exampleAdd(data: any) {
}
/**
* @description: 更新案例
* @param data 案例数据
* @description: 更新案例演示
* @param data 案例演示数据
*/
export function exampleUpdate(data: any) {
return http.request({
@@ -60,8 +60,8 @@ export function exampleUpdate(data: any) {
}
/**
* @description: 删除案例
* @param id 案例ID
* @description: 删除案例演示
* @param id 案例演示ID
*/
export function exampleDelete(id) {
return http.request({
@@ -71,8 +71,8 @@ export function exampleDelete(id) {
}
/**
* @description: 批量删除案例
* @param data 案例ID数组
* @description: 批量删除案例演示
* @param data 案例演示ID数组
*/
export function exampleBatchDelete(data: any) {
return http.request({
Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 381 KiB

+2 -2
View File
@@ -70,9 +70,9 @@
},
});
const upload = ref<UploadInstance>();
const uploadHeaders = reactive({
const uploadHeaders = computed(() => ({
authorization: 'Bearer ' + useUserStore().getToken,
});
}));
const emit = defineEmits(['success', 'update:visible']);
const dialogClose = () => {
+2 -2
View File
@@ -2,10 +2,10 @@
<div class="page-footer">
<div class="page-footer-link">
<a href="https://www.djangoadmin.cn" target="_blank"> 官网 </a>
<a href="http://docs.django.elevue.djangoadmin.cn" target="_blank"> 文档 </a>
<a href="http://docs.flask.elevue.djangoadmin.cn" target="_blank"> 文档 </a>
<a href="https://www.djangoadmin.cn/goods/detail/2" target="_blank"> 授权 </a>
</div>
<div class="copyright">Copyright © 2026 珊瑚云研发团队</div>
<div class="copyright">Copyright © 2026 小蚂蚁云研发团队</div>
</div>
</template>
+1 -1
View File
@@ -20,7 +20,7 @@
*/
const schemas: FormSchema[] = [
{
field: 'password',
field: 'oldPassword',
component: 'Input',
type: 'password',
label: '旧密码',
+1 -1
View File
@@ -12,7 +12,7 @@
>
<div v-if="navMode === 'horizontal'" class="logo">
<img alt="" src="~@/assets/images/logo.png" />
<h2 v-show="!collapsed" class="title">Django+ElementPlus版</h2>
<h2 v-show="!collapsed" class="title">Flask+ElementPlus版</h2>
</div>
</div>
<!--左侧菜单-->
+1 -1
View File
@@ -9,7 +9,7 @@
<h2
v-show="!isCollapsed || navMode === 'horizontal' || (navMode === 'horizontal-mix' && mixMenu)"
class="mt-0 title"
>Django+ElementPlus版</h2
>Flask+ElementPlus版</h2
>
</div>
</template>
+19 -2
View File
@@ -74,11 +74,14 @@
</el-icon>
</div>
</template>
<check-token />
<!-- 全局WebSocket通讯组件 -->
<!-- <global-websocket :uri="'/api/websocket/' + userInfo.id" @rollback="rollback" />
<check-token /> -->
</template>
<script lang="ts" setup>
import { ref, unref, computed, onMounted, watch, provide, defineAsyncComponent } from 'vue';
import { ref, unref, computed, onMounted, watch, provide, h, defineAsyncComponent } from 'vue';
import { ElNotification } from 'element-plus';
import { Logo } from './components/Logo';
import { TabsView } from './components/TagsView';
import { MainView } from './components/Main';
@@ -98,6 +101,7 @@
/**
* 定义参数
*/
const GlobalWebsocket = defineAsyncComponent(() => import('@/components/Websocket/index.vue'));
const CheckToken = defineAsyncComponent(() => import('@/components/CheckToken/index.vue'));
const { getDarkTheme } = useDesignSetting();
const {
@@ -231,6 +235,19 @@
}
};
/**
* 消息通知回调
* @param msg 消息
*/
const rollback = (msg) => {
// ElNotification({
// type: 'info',
// title: '通知',
// message: h('div', msg),
// duration: 5000,
// });
};
/**
* 钩子函数
*/
+1 -1
View File
@@ -39,7 +39,7 @@ export const LoginRoute: RouteRecordRaw = {
name: 'Login',
component: () => import('@/views/login/index.vue'),
meta: {
title: '登录 - Django+ElementPlus前后端分离版本',
title: '登录 - Flask+ElementPlus前后端分离版本',
},
};
+4 -4
View File
@@ -282,11 +282,11 @@
{
icon: BarChartOutlined,
size: '32',
title: '租户管理',
title: '角色管理',
color: '#69c0ff',
eventObject: {
click: () => {
router.push({ path: '/system/tenant' });
router.push({ path: '/system/role' });
},
},
},
@@ -326,11 +326,11 @@
{
icon: MailOutlined,
size: '32',
title: '消息管理',
title: '文件管理',
color: '#5cdbd3',
eventObject: {
click: () => {
router.push({ path: '/data/message' });
router.push({ path: '/file/fileTemplate' });
},
},
},
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="account-container">
<div class="account-wrap-login">
<div class="login-pic">
<h1 class="login-title">Django+ElementPlus前后端分离版本</h1>
<h1 class="login-title">Flask+ElementPlus前后端分离版本</h1>
<h4 class="login-subtitle">数字化赋能,助力企业发展,全方位提供数据中台解决方案!</h4>
</div>
<div class="login-form">
+4 -1
View File
@@ -99,7 +99,7 @@
* 定义操作栏
*/
const actionColumn = reactive({
width: 350,
width: 370,
label: '操作',
prop: 'action',
fixed: 'right',
@@ -109,6 +109,7 @@
actions: [
{
label: '启动',
icon: 'Clock',
type: 'success',
onClick: handelChangeStatus.bind(null, record),
auth: ['sys:job:resume'],
@@ -118,6 +119,7 @@
},
{
label: '暂停',
icon: 'Clock',
type: 'warning',
onClick: handelChangeStatus.bind(null, record),
auth: ['sys:job:pause'],
@@ -258,6 +260,7 @@
const handelChangeStatus = async (record) => {
await confirm(`确定要${record.row.status == 1 ? '暂停' : '启动'}`);
record.row.status == 1 ? await getJobPause(record.row.id) : await getJobResume(record.row.id);
message('执行成功');
reloadTable('noRefresh');
};
const handelRunOnce = async (record) => {
+3 -3
View File
@@ -17,7 +17,7 @@
type="danger"
@click="handleDelete()"
:disabled="!selectionData.length"
v-perm="['sys:operLog:batchDelete']"
v-perm="['sys:operationLog:batchDelete']"
>
<template #icon>
<el-icon class="el-input__icon">
@@ -79,14 +79,14 @@
icon: 'View',
type: 'warning',
onClick: handleDetail.bind(null, record),
auth: ['sys:operLog:detail'],
auth: ['sys:operationLog:detail'],
},
{
label: '删除',
icon: 'Delete',
type: 'danger',
onClick: handleDelete.bind(null, record),
auth: ['sys:operLog:delete'],
auth: ['sys:operationLog:delete'],
},
],
});
+19
View File
@@ -84,6 +84,8 @@
dialogTitle="导入用户"
@success="reloadTable()"
/>
<!-- 发送WebSocket消息 -->
<sendMsgDialog v-if="sendMsgVisible" v-model:visible="sendMsgVisible" :receiveId="receiveId" />
</PageWrapper>
</template>
@@ -108,12 +110,15 @@
* 定义参数
*/
const userId = ref(0);
const receiveId = ref('');
const tableRef = ref();
const editVisible = ref(false);
const sendMsgVisible = ref(false);
const importVisible = ref(false);
const selectionData = ref([]);
const exportLoading = ref(false);
const editDialog = defineAsyncComponent(() => import('./edit.vue'));
const sendMsgDialog = defineAsyncComponent(() => import('./sendMsg.vue'));
const fileUpload = defineAsyncComponent(() => import('@/components/importFile/index.vue'));
/**
@@ -273,6 +278,20 @@
message('导出成功');
};
/**
* 发送消息
* 此处写的是Socket消息发送案例,实际业务研发时根据实际需求自行接入模板消息
*/
const sendMessage = async () => {
if (selectionData.value.length == 0) {
message('请选择数据', 'error');
return;
}
let ids = [];
ids = selectionData.value.map(({ id }) => id);
receiveId.value = ids.join();
sendMsgVisible.value = true;
};
</script>
<style lang="scss" scoped></style>
+4 -4
View File
@@ -66,8 +66,8 @@ export const columns = [
// 状态标签渲染
render(record) {
const statusMap = {
1: { text: '正常', type: 'success' },
2: { text: '禁用', type: 'danger' },
1: { text: '在用', type: 'success' },
2: { text: '停用', type: 'danger' },
};
const status = statusMap[record.row.status];
return h(
@@ -82,7 +82,7 @@ export const columns = [
},
},
{
label: '排序',
label: '案例排序',
prop: 'sort',
minWidth: 100,
// 普通文本渲染
@@ -97,4 +97,4 @@ export const columns = [
prop: 'createTime',
width: 180,
},
];
];
+6 -6
View File
@@ -1,7 +1,7 @@
<template>
<el-dialog
v-model="props.visible"
title="案例详情"
title="案例演示详情"
width="750"
:close-on-click-modal="false"
:before-close="dialogClose"
@@ -26,7 +26,7 @@
<el-descriptions-item label="案例状态:" label-class-name="des-width">
<span>{{ getChoiceLabel('status', formData.status) }}</span>
</el-descriptions-item>
<el-descriptions-item label="排序:" label-class-name="des-width">
<el-descriptions-item label="案例排序:" label-class-name="des-width">
{{ formData.sort || '-' }}
</el-descriptions-item>
</el-descriptions>
@@ -85,8 +85,8 @@
4: '类型4',
},
status: {
1: '正常',
2: '禁用',
1: '在用',
2: '停用',
},
};
return choiceMap[fieldName]?.[value] || value || '-';
@@ -112,7 +112,7 @@
}
}
} catch (error) {
console.error('获取案例详情失败', error);
console.error('获取案例演示详情失败', error);
}
};
@@ -180,4 +180,4 @@
max-width: 100%;
}
}
</style>
</style>
+9 -11
View File
@@ -1,7 +1,7 @@
<template>
<el-dialog
v-model="props.visible"
:title="props.id ? '编辑案例' : '新增案例'"
:title="props.id ? '编辑案例演示' : '新增案例演示'"
width="500"
:close-on-click-modal="false"
:before-close="dialogClose"
@@ -20,6 +20,7 @@
<el-form-item
label="案例图片"
prop="avatar"
:rules="{ required: true, message: '请上传案例图片', trigger: 'change' }"
>
<UploadImg
@changeFileName="(name) => (formData.avatarImgName = name)"
@@ -35,7 +36,6 @@
<el-form-item
label="案例类型"
prop="type"
:rules="{ required: true, message: '请选择案例类型:1-类型1 2-类型2 3-类型3 4-类型4', trigger: 'change' }"
>
<el-select
v-model="formData.type"
@@ -51,22 +51,20 @@
/>
</el-select>
</el-form-item>
<!-- 案例状态:1-正常 2-禁用(单选) -->
<!-- 案例状态:1-在用 2-停用(单选) -->
<el-form-item
label="案例状态"
prop="status"
:rules="{ required: true, message: '请选择案例状态:1-正常 2-禁用', trigger: 'change' }"
>
<el-radio-group v-model="formData.status">
<el-radio :value="1">正常</el-radio>
<el-radio :value="2">禁用</el-radio>
<el-radio :value="1">在用</el-radio>
<el-radio :value="2">停用</el-radio>
</el-radio-group>
</el-form-item>
<!-- 排序(数字输入) -->
<!-- 案例排序(数字输入) -->
<el-form-item
label="排序"
label="案例排序"
prop="sort"
:rules="{ required: true, message: '请输入排序', trigger: 'blur' }"
>
<el-input-number v-model="formData.sort" :min="0" :max="99999" />
</el-form-item>
@@ -194,7 +192,7 @@
}
// 设置富文本编辑器内容
} catch (error) {
console.error('获取案例详情失败', error);
console.error('获取案例演示详情失败', error);
message('获取数据失败', 'error');
}
};
@@ -232,4 +230,4 @@
}
},
);
</script>
</script>
+3 -2
View File
@@ -24,7 +24,7 @@
<PlusOutlined />
</el-icon>
</template>
添加案例
添加案例演示
</el-button>
<!-- 批量删除按钮 -->
@@ -229,7 +229,7 @@
}
}
// 确认删除
await confirm('确定要删除选中的案例吗?');
await confirm('确定要删除选中的案例演示吗?');
// 执行删除操作
record ? await exampleDelete(record.row.id) : await exampleBatchDelete(ids);
message('删除成功');
@@ -243,6 +243,7 @@
function onSelectionChange(value) {
selectionData.value = value;
}
</script>
<style lang="scss" scoped></style>
+3 -3
View File
@@ -50,14 +50,14 @@ export const schemas: FormSchema[] = [
clearable: true,
options: [
{
label: '正常',
label: '在用',
value: 1,
},
{
label: '禁用',
label: '停用',
value: 2,
},
],
},
},
];
];