mirror of
https://gitee.com/niucloud-team/niucloud-admin.git
synced 2026-01-27 21:38:10 +00:00
update admin
This commit is contained in:
parent
1580d8607a
commit
c0ab4b1c69
2
admin/auto-imports.d.ts
vendored
2
admin/auto-imports.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
const ElNotification: typeof import('element-plus/es')['ElNotification']
|
||||
}
|
||||
|
||||
2
admin/components.d.ts
vendored
2
admin/components.d.ts
vendored
@ -32,7 +32,6 @@ declare module '@vue/runtime-core' {
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
@ -70,6 +69,7 @@ declare module '@vue/runtime-core' {
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
|
||||
ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
ElTree: typeof import('element-plus/es')['ElTree']
|
||||
|
||||
@ -5,7 +5,7 @@ import request from '@/utils/request'
|
||||
* @returns
|
||||
*/
|
||||
export function getAddonLocal(params: Record<string, any>) {
|
||||
return request.get('addon/local', params, {showSuccessMessage: true})
|
||||
return request.get('addon/local', params)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -33,13 +33,22 @@ export function installAddon(params: Record<string, any>) {
|
||||
return request.post(`addon/install/${params.addon}`, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装插件
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function cloudInstallAddon(params: Record<string, any>) {
|
||||
return request.post(`addon/cloudinstall/${params.addon}`, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载插件
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function uninstallAddon(params: Record<string, any>) {
|
||||
return request.post(`addon/uninstall/${params.addon}`, params, {showSuccessMessage: true})
|
||||
return request.post(`addon/uninstall/${params.addon}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,23 +57,22 @@ export function uninstallAddon(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function preInstallCheck(addon: string) {
|
||||
return request.get(`addon/install/check/${addon}`, {timeout: 30 * 1000})
|
||||
return request.get(`addon/install/check/${addon}`, { timeout: 30 * 1000 })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件安装任务执行状态
|
||||
* @param addon
|
||||
* @param key
|
||||
* @returns
|
||||
* 获取正在安装的插件
|
||||
* @returns
|
||||
*/
|
||||
export function getAddonInstallTaskState(addon: string, key: string) {
|
||||
return request.get(`addon/install/${addon}/status/${key}`)
|
||||
export function getAddonInstalltask() {
|
||||
return request.get('addon/installtask')
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行安装任务
|
||||
* @param addon
|
||||
* 获取插件云安装日志
|
||||
* @param addon
|
||||
* @returns
|
||||
*/
|
||||
export function executeInstall(addon: string) {
|
||||
return request.post(`addon/install/execute/${addon}`, {})
|
||||
export function getAddonCloudInstallLog(addon: string) {
|
||||
return request.get(`addon/cloudinstall/${addon}`)
|
||||
}
|
||||
88
admin/src/app/api/dict.ts
Normal file
88
admin/src/app/api/dict.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// USER_CODE_BEGIN -- sys_dict
|
||||
|
||||
|
||||
/**
|
||||
* 获取数据字典列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getDictList(params: Record<string, any>) {
|
||||
return request.get(`dict/dict`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据字典详情
|
||||
* @param id 数据字典id
|
||||
* @returns
|
||||
*/
|
||||
export function getDictInfo(id: number) {
|
||||
return request.get(`dict/dict/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据字典
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function addDict(params: Record<string, any>) {
|
||||
return request.post('dict/dict', params, { showErrorMessage: true, showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑数据字典
|
||||
* @param id
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function addDictData(params: Record<string, any>) {
|
||||
return request.put(`dict/dict/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑数据字典
|
||||
* @param id
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function editDict(params: Record<string, any>) {
|
||||
return request.put(`dict/dict/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据字典
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export function deleteDict(id: number) {
|
||||
return request.delete(`dict/dict/${id}`, { showErrorMessage: true, showSuccessMessage: true })
|
||||
}
|
||||
/**
|
||||
* 编辑数据字典内容
|
||||
* @param id
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function setDictData(id:number,params: Record<string, any>) {
|
||||
return request.put(`dict/dictionary/${id}`, params, { showErrorMessage: true,showSuccessMessage: true })
|
||||
}
|
||||
/**
|
||||
* 获取数据字典列表全部
|
||||
* @returns
|
||||
*/
|
||||
export function getDictAll() {
|
||||
return request.get(`dict/all`)
|
||||
}
|
||||
// USER_CODE_END -- sys_dict
|
||||
|
||||
/**
|
||||
* 数据字典关键词查询
|
||||
* @param id
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function useDictionary(type: string) {
|
||||
return request.get(`dict/dictionary/type/${type}`)
|
||||
}
|
||||
|
||||
@ -310,7 +310,7 @@ export function getCashOutList(params: Record<string, any>) {
|
||||
* @param id
|
||||
*/
|
||||
export function getCashOutDetail(id: number) {
|
||||
return request.get(`member/cash_out/${id}`, {})
|
||||
return request.get(`member/cash_out/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -61,7 +61,7 @@ export function getSmsList() {
|
||||
* @returns
|
||||
*/
|
||||
export function getSmsInfo(sms_type: string) {
|
||||
return request.get(`notice/notice/sms/${sms_type}`,)
|
||||
return request.get(`notice/notice/sms/${sms_type}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -4,7 +4,7 @@ import request from '@/utils/request'
|
||||
* 获取插件列表
|
||||
* @returns
|
||||
*/
|
||||
export function getaddonDevelop(params: Record<string, any>) {
|
||||
export function getAddonDevelop(params: Record<string, any>) {
|
||||
return request.get(`addon_develop`,{params});
|
||||
}
|
||||
/**
|
||||
@ -19,7 +19,7 @@ export function getAddontype() {
|
||||
|
||||
* @returns
|
||||
*/
|
||||
export function getAddonDevelop(key:any) {
|
||||
export function getAddonDevelopInfo(key:any) {
|
||||
return request.get(`addon_develop/${key}`)
|
||||
}
|
||||
/**
|
||||
@ -147,13 +147,13 @@ export function getSystem() {
|
||||
* 获取全部模型
|
||||
*/
|
||||
export function getGeneratorAllModel(params:any) {
|
||||
return request.get(`generator/all_model`,params)
|
||||
return request.get(`generator/all_model`,{params})
|
||||
}
|
||||
/**
|
||||
* 获取 表字段
|
||||
*/
|
||||
export function getGeneratorTableColumn(params:any){
|
||||
return request.get(`generator/table_column`,params)
|
||||
return request.get(`generator/table_column`,{params})
|
||||
}
|
||||
/**
|
||||
* 同步校验
|
||||
|
||||
@ -33,5 +33,11 @@
|
||||
"addonVersion": "插件版本",
|
||||
"versionCode": "版本号",
|
||||
"createTime": "发布时间",
|
||||
"buyLabel": "已购买"
|
||||
"buyLabel": "已购买",
|
||||
"installTips": "安装后需手动更新插件引用的依赖和编译各个端口的前端源码",
|
||||
"localInstall":"本地安装",
|
||||
"cloudInstall": "一键云安装",
|
||||
"cloudInstallTips": "云安装可实现一键安装,安装后无需手动更新依赖和编译前端源码",
|
||||
"installingTips": "有插件正在安装中请等待安装完成之后再进行其他操作,点击查看",
|
||||
"installPercent": "安装进度"
|
||||
}
|
||||
@ -33,5 +33,6 @@
|
||||
"preview": "预览",
|
||||
"authTips": "上传代码需先绑定授权码,如果已有授权请先进行绑定,没有授权可到niucloud官网购买云服务之后再进行操作",
|
||||
"toBind": "绑定授权",
|
||||
"toNiucloud": "去niucloud官网"
|
||||
"toNiucloud": "去niucloud官网",
|
||||
"failReason": "失败原因:"
|
||||
}
|
||||
23
admin/src/app/lang/zh-cn/dict.list.json
Normal file
23
admin/src/app/lang/zh-cn/dict.list.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name":"字典名称",
|
||||
"namePlaceholder":"请输入字典名称",
|
||||
"key":"字典关键词",
|
||||
"keyPlaceholder":"请输入字典关键词",
|
||||
"data":"字典数据",
|
||||
"dataPlaceholder":"请输入字典数据",
|
||||
"memo":"备注",
|
||||
"memoPlaceholder":"请输入备注",
|
||||
"addDict":"添加数据字典",
|
||||
"updateDict":"编辑数据字典",
|
||||
"dictDeleteTips":"确定要删除该数据吗?",
|
||||
"dictData":"数据管理",
|
||||
"addDictData":"添加数据",
|
||||
"editDictData":"编辑数据",
|
||||
"dataName":"数据名称",
|
||||
"dataNamePlaceholder":"请输入数据名称",
|
||||
"dataValue":"数据值",
|
||||
"dataValuePlaceholder":"请输入数据值",
|
||||
"sortPlaceholder":"数值越大越排前",
|
||||
"momePlaceholder":"请输入备注",
|
||||
"createTime":"创建时间"
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
"keyPlaceholder":"请输入插件标识",
|
||||
"keyPlaceholderErr":"插件标识格式不正确,只能以字母开头且只能输入字母、数字、下划线",
|
||||
"keyPlaceholder1":"插件标识指开发插件的文件夹名称,申请之后不能修改(只能包括字母、数字和下划线,且只能以字母开头,格式如:f1111、f11_22)",
|
||||
"keyPlaceholder2":"设置后要检测标识与niucloud应用是否重复,如果重复则无法进行上传发布",
|
||||
"keyPlaceholder2":"插件标识设置后建议进行插件标识检测,如果当前插件标识已经在niucloud官方市场注册,则只能在本地使用,无法在官方市场发布销售",
|
||||
"desc":"插件描述",
|
||||
"descPlaceholder":"请输入插件描述",
|
||||
"author":"作者",
|
||||
@ -24,10 +24,11 @@
|
||||
"typePlaceholder":"请选择插件类型",
|
||||
"typePlaceholder1":"应用:指独立开发的系统,比如商城,零售,erp等",
|
||||
"typePlaceholder2":"插件:指不是独立的系统,可以是辅助应用的插件比如商城的拼团,也可以是独立的插件比如系统表单等",
|
||||
"supportApp":"前置插件",
|
||||
"supportAppPlaceholder":"请输入前置插件",
|
||||
"supportType":"所属应用",
|
||||
"supportApp":"支持应用",
|
||||
"supportAppPlaceholder":"请选择支持应用",
|
||||
"GeneratePlugins":"生成插件",
|
||||
"successText":"当前插件标识无重复",
|
||||
"warningText":"当前插件标识重复,继续使用则无法进行上传发布",
|
||||
"successText":"检测当前插件标识尚未在应用市场注册,插件开发后可以在niucloud官方市场发布",
|
||||
"warningText":"检测到当前插件标识已经在niucloud官方市场注册,开发的插件只能在本地使用,无法在官方市场发布销售",
|
||||
"onSaveSuccessText":"插件生成成功"
|
||||
}
|
||||
@ -18,6 +18,7 @@
|
||||
"tableNamePlaceholder":"请输入表名",
|
||||
"tableContentPlaceholder":"请输入描述",
|
||||
"addonPlaceholder":"请选择插件",
|
||||
"addonPlaceholder1":"/",
|
||||
"moduleNamePlaceholder":"请输入模块名",
|
||||
"classNamePlaceholder":"请输入类名",
|
||||
"editTypePlaceholder":"请选择编辑方式",
|
||||
@ -52,6 +53,7 @@
|
||||
"isQuery":"查询",
|
||||
"queryType":"搜索方式",
|
||||
"formType": "表单类型",
|
||||
"verifyType": "验证类型",
|
||||
"formInput":"文本框",
|
||||
"formTextarea":"文本域",
|
||||
"formSelect":"下拉框",
|
||||
@ -60,6 +62,7 @@
|
||||
"formDateTime":"日期",
|
||||
"formImageSelect":"图片上传",
|
||||
"formEditor":"富文本",
|
||||
"formNumber":"数字框",
|
||||
"pk":"主键",
|
||||
"status": "状态",
|
||||
"string": "字符串",
|
||||
@ -102,8 +105,32 @@
|
||||
"foreignKeyPlaceholder":"请输入外键",
|
||||
"addons":"关联应用",
|
||||
"addonsPlaceholder":"请选择应用",
|
||||
"saveAndSync":"保存并同步",
|
||||
"saveAndDownload":"保存并下载",
|
||||
"saveAndSync":"同步代码",
|
||||
"saveAndDownload":"下载代码",
|
||||
"saveAndSyncText":"同步的代码与项目产生冲突,是否确认覆盖?",
|
||||
"saveAndSyncText1":"同步的代码会加入到项目代码中,是否确认继续"
|
||||
"saveAndSyncText1":"同步的代码会加入到项目代码中,是否确认继续",
|
||||
"mobileVerify":"手机号验证",
|
||||
"numberVerify":"整数验证",
|
||||
"idCardVerify":"身份证验证",
|
||||
"emailVerify":"邮箱验证",
|
||||
"maxVerify":"最大输入120个字符",
|
||||
"minVerify":"最小输入1个字符",
|
||||
"maxLabel":"最大输入字符",
|
||||
"minLabel":"最小输入字符",
|
||||
"minPlaceholder":"最小输入字符不能为空",
|
||||
"minPlaceholder1":"最小输入字符不可大于最大输入字符",
|
||||
"maxPlaceholder":"最大字输入字符不能为空",
|
||||
"maxPlaceholder1":"最大输入字符不可小于最小输入字符",
|
||||
"maxLabel1":"最大输入数",
|
||||
"minLabel1":"最小输入数",
|
||||
"min1Placeholder":"最小输入数不能为空",
|
||||
"min1Placeholder1":"最小输入数不可大于最大输入数",
|
||||
"max1Placeholder":"最大字输入数不能为空",
|
||||
"max1Placeholder1":"最大输入数不可小于最小输入数",
|
||||
"between":"输入字符区间",
|
||||
"setUp":"设置",
|
||||
"dictType":"数据字典",
|
||||
"dictTypePlaceholder":"请选择数据字典",
|
||||
"dictTypePlaceholder1":"部分字段未选择数据字典"
|
||||
|
||||
}
|
||||
@ -44,5 +44,6 @@
|
||||
"btn5":"官方市场",
|
||||
"saveAndSync":"同步代码",
|
||||
"saveAndSyncText":"同步的代码与项目产生冲突,是否确认覆盖?",
|
||||
"saveAndSyncText1":"同步的代码会加入到项目代码中,是否确认继续"
|
||||
"saveAndSyncText1":"同步的代码会加入到项目代码中,是否确认继续",
|
||||
"addonName": "所属插件"
|
||||
}
|
||||
@ -227,10 +227,8 @@
|
||||
<el-scrollbar max-height="50vh">
|
||||
<div class="min-h-[150px]">
|
||||
<div class="bg-[#fff] my-3" v-if="installCheckResult.dir">
|
||||
<el-alert :title="t('jobError')" type="error" :closable="false" class="mt-[20px]"
|
||||
v-if="!installCheckResult.job_normal" />
|
||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
||||
<div class="px-[20px] text-[14px]">
|
||||
<div class="px-[20px] pt-[10px] text-[14px]">
|
||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||
<el-col :span="12">
|
||||
<span>{{ t('path') }}</span>
|
||||
@ -277,45 +275,27 @@
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-[#fff] my-3">
|
||||
<p class="pl-[20px] ">{{ t('process') }}</p>
|
||||
<div class="px-[20px] text-[14px]">
|
||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||
<el-col :span="12">
|
||||
<span>{{ t('name') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('demand') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('status') }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]">
|
||||
<el-col :span="12">
|
||||
<span>php think queue:listen</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('open') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="installCheckResult.job_normal">
|
||||
<el-icon color="green"><Select /></el-icon>
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="flex justify-end">
|
||||
<el-button type="primary" :disabled="!installCheckResult.is_pass" @click="handleInstall">{{ t('install')
|
||||
}}</el-button>
|
||||
<div class="flex justify-end" v-if="mode == 'development'">
|
||||
<el-tooltip effect="dark" :content="t('installTips')" placement="top">
|
||||
<el-button type="default" :disabled="!installCheckResult.is_pass || cloudInstalling"
|
||||
:loading="localInstalling" @click="handleInstall">{{
|
||||
t('localInstall')
|
||||
}}</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" :content="t('cloudInstallTips')" placement="top">
|
||||
<el-button type="primary" :disabled="!installCheckResult.is_pass || localInstalling"
|
||||
:loading="cloudInstalling" @click="handleCloudInstall">{{
|
||||
t('cloudInstall')
|
||||
}}</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="flex justify-end" v-else>
|
||||
<el-button type="primary" :disabled="!installCheckResult.is_pass" :loading="cloudInstalling"
|
||||
@click="handleCloudInstall">{{
|
||||
t('cloudInstall')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="installStep == 1" class="h-[50vh] mt-[20px]">
|
||||
@ -334,17 +314,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { ref, watch, computed, h } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, getAddonInstallTaskState, executeInstall } from '@/app/api/addon'
|
||||
import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, cloudInstallAddon, getAddonInstalltask, getAddonCloudInstallLog } from '@/app/api/addon'
|
||||
import { downloadVersion } from '@/app/api/module'
|
||||
import { TabsPaneContext, ElMessageBox } from 'element-plus'
|
||||
import { TabsPaneContext, ElMessageBox, ElNotification } from 'element-plus'
|
||||
import { img } from '@/utils/common'
|
||||
import { Terminal, api as terminalApi } from 'vue-web-terminal'
|
||||
|
||||
const activeName = ref('installed')
|
||||
const loading = ref<Boolean>(false)
|
||||
const showType = ref('large')
|
||||
const mode = ref(import.meta.env.MODE)
|
||||
|
||||
const downEvent = (key: string) => {
|
||||
downloadVersion(key).then(() => {
|
||||
@ -417,102 +398,135 @@ const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
const currAddon = ref('')
|
||||
// 安装面板弹窗
|
||||
const installShowDialog = ref(false)
|
||||
// 安装任务
|
||||
let installTask: AnyObject = {}
|
||||
// 当前查询的任务
|
||||
let currTask: string = ''
|
||||
// 已执行任务
|
||||
let executedTask: string[] = []
|
||||
// 安装步骤
|
||||
const installStep = ref(0)
|
||||
// 安装检测结果
|
||||
const installCheckResult = ref({})
|
||||
// 安装警告
|
||||
const installWarning = ref<string[]>([])
|
||||
// 定时器对象
|
||||
let timer: null | any = null
|
||||
|
||||
/**
|
||||
* 安装
|
||||
* @param key
|
||||
*/
|
||||
const installAddonFn = (key: string) => {
|
||||
currAddon.value = key
|
||||
installAddon({ addon: key }).then(res => {
|
||||
installStep.value = 0
|
||||
executedTask = []
|
||||
installWarning.value = []
|
||||
installTask = makeIterator(Object.keys(res.data))
|
||||
currTask = installTask.next().value
|
||||
installShowDialog.value = true
|
||||
installStep.value = 0
|
||||
installWarning.value = []
|
||||
installShowDialog.value = true
|
||||
|
||||
preInstallCheck(key).then(res => {
|
||||
installCheckResult.value = res.data
|
||||
}).catch(() => { })
|
||||
}).catch(() => {
|
||||
})
|
||||
preInstallCheck(key).then(res => {
|
||||
installCheckResult.value = res.data
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建遍历器
|
||||
* @param arr array
|
||||
* 获取正在进行的安装任务
|
||||
*/
|
||||
const makeIterator = (arr: string[]) => {
|
||||
let nextIndex = 0
|
||||
return {
|
||||
next: function () {
|
||||
return nextIndex < arr.length
|
||||
? { value: arr[nextIndex++] }
|
||||
: { done: true }
|
||||
let notificationEl = null
|
||||
const getInstallTask = (first: boolean = true) => {
|
||||
getAddonInstalltask().then(res => {
|
||||
if (res.data) {
|
||||
if (first) {
|
||||
installLog = []
|
||||
currAddon.value = res.data.addon
|
||||
if (!installShowDialog.value) {
|
||||
notificationEl = ElNotification.success({
|
||||
title: t('warning'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: h('div', {}, [
|
||||
t('installingTips'),
|
||||
h('span', { class: 'text-primary cursor-pointer', onClick: checkInstallTask }, [t('installPercent')])
|
||||
]),
|
||||
duration: 0,
|
||||
showClose: false
|
||||
})
|
||||
}
|
||||
}
|
||||
if (res.data.error) {
|
||||
return
|
||||
}
|
||||
if (res.data.mode == 'cloud') {
|
||||
getCloudInstallLog()
|
||||
}
|
||||
setTimeout(() => {
|
||||
getInstallTask(false)
|
||||
}, 2000)
|
||||
} else {
|
||||
if (!first) {
|
||||
installStep.value += 2
|
||||
localListFn()
|
||||
notificationEl.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
getInstallTask()
|
||||
|
||||
const checkInstallTask = () => {
|
||||
installShowDialog.value = true
|
||||
installStep.value = 1
|
||||
}
|
||||
|
||||
const localInstalling = ref(false)
|
||||
/**
|
||||
* 安装插件
|
||||
*/
|
||||
const handleInstall = () => {
|
||||
if (!installCheckResult.value.is_pass) return
|
||||
installStep.value += 1
|
||||
if (!installCheckResult.value.is_pass || localInstalling.value) return
|
||||
localInstalling.value = true
|
||||
|
||||
terminalApi.execute('my-terminal', 'clear')
|
||||
installAddon({ addon: currAddon.value }).then(res => {
|
||||
installStep.value += 2
|
||||
localListFn()
|
||||
localInstalling.value = false
|
||||
}).catch((res) => {
|
||||
localInstalling.value = false
|
||||
})
|
||||
}
|
||||
|
||||
executeInstall(currAddon.value)
|
||||
.then(() => {
|
||||
timer = setInterval(() => {
|
||||
getAddonInstallTaskState(currAddon.value, currTask).then(({ data }) => {
|
||||
if (!executedTask.includes(currTask)) {
|
||||
terminalApi.execute('my-terminal', data.command)
|
||||
executedTask.push(currTask)
|
||||
}
|
||||
switch (data.state) {
|
||||
case 'success':
|
||||
terminalApi.pushMessage('my-terminal', { content: `${data.desc}执行成功`, class: 'success' })
|
||||
if (data.step == 'installComplete') {
|
||||
clearInterval(timer)
|
||||
installStep.value += 2
|
||||
localListFn()
|
||||
} else {
|
||||
currTask = installTask.next().value
|
||||
}
|
||||
break
|
||||
case 'fail':
|
||||
terminalApi.pushMessage('my-terminal', { content: `${data.desc}执行失败`, class: 'error' })
|
||||
terminalApi.pushMessage('my-terminal', { content: `失败原因:${data.error}` })
|
||||
clearInterval(timer)
|
||||
break
|
||||
case 'warn':
|
||||
terminalApi.pushMessage('my-terminal', { content: data.error, class: 'warning' })
|
||||
installWarning.value.push(data.error)
|
||||
break
|
||||
const cloudInstalling = ref(false)
|
||||
/**
|
||||
* 云安装插件
|
||||
*/
|
||||
const handleCloudInstall = () => {
|
||||
if (!installCheckResult.value.is_pass || cloudInstalling.value) return
|
||||
cloudInstalling.value = true
|
||||
|
||||
cloudInstallAddon({ addon: currAddon.value }).then(res => {
|
||||
installStep.value += 1
|
||||
terminalApi.execute('my-terminal', 'clear')
|
||||
terminalApi.pushMessage('my-terminal', { content: '开始安装插件', class: 'info' })
|
||||
getInstallTask()
|
||||
cloudInstalling.value = false
|
||||
}).catch((res) => {
|
||||
cloudInstalling.value = false
|
||||
})
|
||||
}
|
||||
|
||||
let installLog: string[] = []
|
||||
const getCloudInstallLog = () => {
|
||||
getAddonCloudInstallLog(currAddon.value)
|
||||
.then(res => {
|
||||
const data = res.data.data ?? []
|
||||
if (data[0] && data[0].length && installShowDialog.value == true) {
|
||||
data[0].forEach(item => {
|
||||
if (!installLog.includes(item.action)) {
|
||||
terminalApi.pushMessage('my-terminal', { content: `正在执行:${item.action}` })
|
||||
installLog.push(item.action)
|
||||
|
||||
if (item.code == 0) {
|
||||
terminalApi.pushMessage('my-terminal', { content: item.msg, class: 'error' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 2000)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
notificationEl?.close()
|
||||
})
|
||||
.catch()
|
||||
}
|
||||
|
||||
// 监听安装弹窗关闭
|
||||
watch(installShowDialog, (nval) => {
|
||||
if (!installShowDialog.value) clearInterval(timer)
|
||||
})
|
||||
|
||||
watch(currAddon, (nval) => {
|
||||
installCheckResult.value = {}
|
||||
})
|
||||
|
||||
@ -91,7 +91,7 @@ import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import selectMenuItem from './select-menu-item.vue'
|
||||
import { addMenu, editMenu, getMenuInfo, getSystemMenu,getAddonMenu } from '@/app/api/sys'
|
||||
import { getaddonDevelop } from '@/app/api/tools'
|
||||
import { getAddonDevelop } from '@/app/api/tools'
|
||||
const showDialog = ref(false)
|
||||
const method = ref('post')
|
||||
const loading = ref(false)
|
||||
@ -169,8 +169,8 @@ const formRules = computed(() => {
|
||||
}
|
||||
})
|
||||
//获取插件列表
|
||||
const getaddonDevelopFn = async () => {
|
||||
let { data } = await getaddonDevelop({})
|
||||
const getAddonDevelopFn = async () => {
|
||||
let { data } = await getAddonDevelop({})
|
||||
addonLst.value = [{ title: "系统", key: "" }]
|
||||
addonLst.value.push(...data)
|
||||
}
|
||||
@ -226,7 +226,7 @@ const setFormData = async (row: any = null) => {
|
||||
loading.value = true
|
||||
Object.assign(formData, initialFormData)
|
||||
popTitle = t('addMenu')
|
||||
getaddonDevelopFn()
|
||||
getAddonDevelopFn()
|
||||
getSystemMenuFn()
|
||||
if (row.menu_key) {
|
||||
popTitle = t('updateMenu')
|
||||
|
||||
@ -26,7 +26,12 @@
|
||||
</template>
|
||||
<el-table-column prop="version" :label="t('code')" align="left" />
|
||||
<!-- <el-table-column prop="desc" :label="t('content')" align="left" /> -->
|
||||
<el-table-column prop="status_name" :label="t('status')" align="left" />
|
||||
<el-table-column prop="status_name" :label="t('status')" align="left">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.status_name }}</div>
|
||||
<div class="text-error" v-if="row.status == -1">{{ t('failReason') }}{{ row.fail_reason }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" :label="t('createTime')" align="center" />
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
|
||||
<template #default="{ row, $index }">
|
||||
|
||||
181
admin/src/app/views/dict/components/dict.vue
Normal file
181
admin/src/app/views/dict/components/dict.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="t('dictData')" width="60%" class="diy-dialog-wrap" :destroy-on-close="true">
|
||||
<div class="mb-[10px]">
|
||||
<el-button type="primary" @click="addEvent">
|
||||
{{ t('addDictData') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="tableDate" size="large" v-loading="loading">
|
||||
<el-table-column :label="t('dataName')" prop="name" />
|
||||
<el-table-column :label="t('dataValue')" prop="value" />
|
||||
<el-table-column :label="t('sort')" align="center" min-width="100px" prop="sort" />
|
||||
<el-table-column :label="t('memo')" prop="memo" />
|
||||
<el-table-column :label="t('operation')" fixed="right" width="120">
|
||||
<template #default="{ row, $index }">
|
||||
<el-button type="primary" link @click="editEvent(row, $index)">{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent($index)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="confirm()">{{
|
||||
t('confirm')
|
||||
}}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
<el-dialog v-model="dialogVisible" :title="type != 'edit' ? t('addDictData') : t('editDictData')" width="480"
|
||||
class="diy-dialog-wrap" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form">
|
||||
<el-form-item :label="t('name')">
|
||||
<el-input v-model="name" disabled class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dataName')" prop="name">
|
||||
<el-input v-model="formData.name" clearable :placeholder="t('dataNamePlaceholder')"
|
||||
class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dataValue')" prop="value">
|
||||
<el-input v-model="formData.value" clearable :placeholder="t('dataValuePlaceholder')"
|
||||
class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('sort')" prop="sort">
|
||||
<div>
|
||||
<el-input-number v-model="formData.sort" ::step="1" step-strictly :value-on-clear="0" :min="0" class="input-width" />
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('sortPlaceholder') }}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('memo')">
|
||||
<el-input v-model="formData.memo" type="textarea" clearable :placeholder="t('momePlaceholder')"
|
||||
class="input-width" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit(formRef)">{{
|
||||
t('confirm')
|
||||
}}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { setDictData, getDictInfo } from '@/app/api/dict'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
let showDialog = ref(false)
|
||||
const loading = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const tableDate = ref<Array<any>>([])
|
||||
const id = ref()
|
||||
const type = ref('add')
|
||||
const formRef = ref()
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const name = ref('')
|
||||
const initialFormData = {
|
||||
name: '',
|
||||
value: '',
|
||||
sort: 0,
|
||||
memo:'',
|
||||
}
|
||||
const formData = ref({ ...initialFormData })
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
name: [
|
||||
{ required: true, message: t('dataNamePlaceholder'), trigger: 'blur' }
|
||||
|
||||
],
|
||||
value: [
|
||||
{ required: true, message: t('dataValuePlaceholder'), trigger: 'blur' }
|
||||
|
||||
],
|
||||
}
|
||||
})
|
||||
const addEvent = () => {
|
||||
type.value = 'add'
|
||||
formData.value = cloneDeep(initialFormData)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const tabelIndex = ref(0)
|
||||
const editEvent = (row: any, index: number) => {
|
||||
type.value = 'edit'
|
||||
tabelIndex.value = index
|
||||
formData.value = cloneDeep(initialFormData)
|
||||
formData.value = Object.assign(formData.value,cloneDeep( row))
|
||||
dialogVisible.value = true
|
||||
}
|
||||
/**
|
||||
* 表单确认
|
||||
*/
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (type.value != 'edit') {
|
||||
tableDate.value.push(cloneDeep(formData.value))
|
||||
} else {
|
||||
tableDate.value.splice(tabelIndex.value, 1, cloneDeep(formData.value))
|
||||
|
||||
}
|
||||
tableDate.value.sort(function(a,b){return b.sort-a.sort})
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
const emit = defineEmits(['complete'])
|
||||
|
||||
/**
|
||||
*删除
|
||||
*/
|
||||
const deleteEvent = (index: number) => {
|
||||
tableDate.value.splice(index, 1)
|
||||
}
|
||||
/**
|
||||
* 确认
|
||||
* @param formEl
|
||||
*/
|
||||
const confirm = async () => {
|
||||
loading.value = true
|
||||
setDictData(id.value, { dictionary: JSON.stringify(tableDate.value) }).then(res => {
|
||||
loading.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const setFormData = async (row: any = null) => {
|
||||
showDialog.value = true
|
||||
loading.value = true
|
||||
id.value = row.id
|
||||
name.value = row.name
|
||||
const data = await (await getDictInfo(row.id)).data
|
||||
tableDate.value = data.dictionary
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
defineExpose({
|
||||
showDialog,
|
||||
setFormData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss">
|
||||
.diy-dialog-wrap .el-form-item__label {
|
||||
height: auto !important;
|
||||
}
|
||||
</style>
|
||||
176
admin/src/app/views/dict/components/edit.vue
Normal file
176
admin/src/app/views/dict/components/edit.vue
Normal file
@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="formData.id ? t('updateDict') : t('addDict')" width="480" class="diy-dialog-wrap"
|
||||
:destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-form-item :label="t('name')" prop="name">
|
||||
<el-input v-model="formData.name" clearable :placeholder="t('namePlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('key')" prop="key">
|
||||
<el-input v-model="formData.key" clearable :placeholder="t('keyPlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('memo')">
|
||||
<el-input v-model="formData.memo" type="textarea" clearable :placeholder="t('memoPlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
|
||||
t('confirm')
|
||||
}}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { addDict, editDict, getDictInfo } from '@/app/api/dict'
|
||||
|
||||
let showDialog = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const initialFormData = {
|
||||
id: '',
|
||||
name: '',
|
||||
key: '',
|
||||
memo: '',
|
||||
|
||||
}
|
||||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
name: [
|
||||
{ required: true, message: t('namePlaceholder'), trigger: 'blur' }
|
||||
|
||||
]
|
||||
,
|
||||
key: [
|
||||
{ required: true, message: t('keyPlaceholder'), trigger: 'blur' }
|
||||
|
||||
]
|
||||
,
|
||||
data: [
|
||||
{ required: true, message: t('dataPlaceholder'), trigger: 'blur' }
|
||||
|
||||
]
|
||||
,
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['complete'])
|
||||
|
||||
/**
|
||||
* 确认
|
||||
* @param formEl
|
||||
*/
|
||||
const confirm = async (formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
let save = formData.id ? editDict : addDict
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
let data = formData
|
||||
|
||||
save(data).then(res => {
|
||||
loading.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(err => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setFormData = async (row: any = null) => {
|
||||
Object.assign(formData, initialFormData)
|
||||
loading.value = true
|
||||
if(row){
|
||||
const data = await (await getDictInfo(row.id)).data
|
||||
if (data) Object.keys(formData).forEach((key: string) => {
|
||||
if (data[key] != undefined) formData[key] = data[key]
|
||||
})
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 验证手机号格式
|
||||
const mobileVerify = (rule: any, value: any, callback: any) => {
|
||||
if (value && !/^1[3-9]\d{9}$/.test(value)) {
|
||||
callback(new Error(t('generateMobile')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证身份证号
|
||||
const idCardVerify = (rule: any, value: any, callback: any) => {
|
||||
if (value && !/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value)) {
|
||||
callback(new Error(t('generateIdCard')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证邮箱号
|
||||
const emailVerify = (rule: any, value: any, callback: any) => {
|
||||
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
|
||||
callback(new Error(t('generateEmail')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证至少输入1个字符
|
||||
const minInputVerify = (rule: any, value: any, callback: any) => {
|
||||
if (value && !/^\d{0,}$/.test(value)) {
|
||||
callback(new Error(t('generateMin')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证最多输入150个字符
|
||||
const maxInputVerify = (rule: any, value: any, callback: any) => {
|
||||
if (value && !/^\d{0,150}$/.test(value)) {
|
||||
callback(new Error(t('generateMax')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证请输入整数
|
||||
const numberVerify = (rule: any, value: any, callback: any) => {
|
||||
if (!Number.isInteger(value)) {
|
||||
callback(new Error(t('generateNumber')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
showDialog,
|
||||
setFormData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss">
|
||||
.diy-dialog-wrap .el-form-item__label{
|
||||
height: auto !important;
|
||||
}
|
||||
</style>
|
||||
169
admin/src/app/views/dict/list.vue
Normal file
169
admin/src/app/views/dict/list.vue
Normal file
@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-[20px]">{{pageName}}</span>
|
||||
<el-button type="primary" @click="addEvent">
|
||||
{{ t('addDict') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="dictTable.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('name')" prop="name">
|
||||
<el-input v-model="dictTable.searchParam.name" :placeholder="t('namePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('key')" prop="key">
|
||||
<el-input v-model="dictTable.searchParam.key" :placeholder="t('keyPlaceholder')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadDictList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="dictTable.data" size="large" v-loading="dictTable.loading">
|
||||
<template #empty>
|
||||
<span>{{ !dictTable.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="name" :label="t('name')" min-width="120" />
|
||||
<el-table-column prop="key" :label="t('key')" min-width="120" />
|
||||
<el-table-column prop="memo" :label="t('memo')" min-width="120" />
|
||||
<el-table-column prop="create_time" :label="t('createTime')" min-width="120" />
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="dictData(row)">{{ t('dictData') }}</el-button>
|
||||
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="dictTable.page" v-model:page-size="dictTable.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="dictTable.total"
|
||||
@size-change="loadDictList()" @current-change="loadDictList" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<edit ref="editDictDialog" @complete="loadDictList" />
|
||||
<dict ref="dictDialog" @complete="loadDictList" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getDictList, deleteDict } from '@/app/api/dict'
|
||||
import { img } from '@/utils/common'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import Edit from '@/app/views/dict/components/edit.vue'
|
||||
import dict from '@/app/views/dict/components/dict.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title;
|
||||
|
||||
let dictTable = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam:{
|
||||
"name":"",
|
||||
"key":""
|
||||
}
|
||||
})
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
/**
|
||||
* 获取数据字典列表
|
||||
*/
|
||||
const loadDictList = (page: number = 1) => {
|
||||
dictTable.loading = true
|
||||
dictTable.page = page
|
||||
|
||||
getDictList({
|
||||
page: dictTable.page,
|
||||
limit: dictTable.limit,
|
||||
...dictTable.searchParam
|
||||
}).then(res => {
|
||||
dictTable.loading = false
|
||||
dictTable.data = res.data.data
|
||||
dictTable.total = res.data.total
|
||||
}).catch(() => {
|
||||
dictTable.loading = false
|
||||
})
|
||||
}
|
||||
loadDictList()
|
||||
|
||||
const editDictDialog: Record<string, any> | null = ref(null)
|
||||
|
||||
/**
|
||||
* 添加数据字典
|
||||
*/
|
||||
const addEvent = () => {
|
||||
editDictDialog.value.setFormData()
|
||||
editDictDialog.value.showDialog = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑数据字典
|
||||
* @param data
|
||||
*/
|
||||
const editEvent = (data: any) => {
|
||||
editDictDialog.value.setFormData(data)
|
||||
editDictDialog.value.showDialog = true
|
||||
}
|
||||
|
||||
const dictDialog: Record<string, any> | null = ref(null)
|
||||
|
||||
const dictData = (data: any) => {
|
||||
dictDialog.value.setFormData(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据字典
|
||||
*/
|
||||
const deleteEvent = (id: number) => {
|
||||
ElMessageBox.confirm(t('dictDeleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
deleteDict(id).then(() => {
|
||||
loadDictList()
|
||||
}).catch(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
loadDictList()
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 多行超出隐藏 */
|
||||
.multi-hidden {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
@ -14,30 +14,24 @@
|
||||
</div>
|
||||
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="applyList.list.length">
|
||||
<div v-for="(item, index) in applyList.list" :key="index + 'b'">
|
||||
<div v-if="appLink[item.key]" class="relative app-item cursor-pointer px-4 mr-4 mt-[20px] bg-[#f7f7f7] border-[1px] hover:border-primary">
|
||||
<div @click="toLink(item.key)">
|
||||
<div class="flex py-5 items-center">
|
||||
<div class="flex justify-center items-center">
|
||||
<el-image class="w-[50px] h-[50px]" :src="img(item.icon)" fit="contain">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<img class="w-[50px] h-[50px]" src="@/app/assets/images/index/app_default.png" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between text-left w-[190px]">
|
||||
<p class="app-text w-[190px] text-[17px] text-[#222] pl-3">{{ item.title }}</p>
|
||||
|
||||
</div>
|
||||
<div v-if="appLink[item.key] && item.type == 'addon'" class="relative app-item cursor-pointer px-4 mr-4 mt-[20px] bg-[#f7f7f7] border-[1px] hover:border-primary">
|
||||
<div @click="toLink(item.key)" class="flex py-5 items-center">
|
||||
<div class="flex justify-center items-center">
|
||||
<el-image class="w-[40px] h-[40px]" :src="img(item.icon)" fit="contain">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<img class="w-[50px] h-[50px]" src="@/app/assets/images/index/app_default.png" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="border-t-[1px] border-[#e8e9eb] py-3">
|
||||
<p class="app-text text-[14px] text-[#999] w-[200px]">{{ item.desc }}</p>
|
||||
<div class="flex flex-col justify-between text-left w-[190px]">
|
||||
<p class="app-text w-[190px] text-[17px] text-[#222] pl-3">{{ item.title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="with-ite absolute top-0 right-0 flex flex-col hidden">
|
||||
<!-- <div class="with-ite absolute top-0 right-0 flex flex-col hidden">
|
||||
<span class="block pr-4 mt-3" :class="item.is_star == 2 ? 'text-primary' : 'text-[#999]'" @click.stop="withEvent(item.key)"><el-icon size="18px"><StarFilled /></el-icon></span>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,6 +52,7 @@ import { findFirstValidRoute } from '@/router/routers'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { t } from '@/lang'
|
||||
import storage from '@/utils/storage'
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const applyList = reactive({
|
||||
@ -89,6 +84,7 @@ const getAppLink = () => {
|
||||
getAppLink()
|
||||
|
||||
const toLink = (addon: string) => {
|
||||
storage.set({ key: 'plugMenuTypeStorage', data: addon })
|
||||
let data = userStore.appMenuList
|
||||
if(!data.length){
|
||||
data.push(addon)
|
||||
|
||||
@ -8,12 +8,13 @@
|
||||
<img v-else src="@/app/assets/images/login/login_index_left.png" alt="">
|
||||
</div>
|
||||
<div class="login flex flex-col w-[400px] h-[400px] p-[40px]">
|
||||
<h3 class="text-center text-lg font-bold mb-[10px]">{{ webSite.site_name || t('siteTitle') }}</h3>
|
||||
<h3 class="text-center text-lg font-bold mb-[10px]">{{ webSite.site_name || t('siteTitle') }}</h3>
|
||||
<h3 class="text-center text-2xl font-bold mb-[26px]">{{ t('platform') }}</h3>
|
||||
|
||||
<el-form :model="form" ref="formRef" :rules="formRules">
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="form.username" :placeholder="t('userPlaceholder')" @keyup.enter="handleLogin(formRef)" class="h-[40px] input-with-select">
|
||||
<el-input v-model="form.username" :placeholder="t('userPlaceholder')"
|
||||
@keyup.enter="handleLogin(formRef)" class="h-[40px] input-with-select">
|
||||
<template #prepend>
|
||||
<icon name="element-User" />
|
||||
</template>
|
||||
@ -21,7 +22,9 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="form.password" :placeholder="t('passwordPlaceholder')" type="password" @keyup.enter="handleLogin(formRef)" :show-password="true" class="h-[40px] input-with-select">
|
||||
<el-input v-model="form.password" :placeholder="t('passwordPlaceholder')" type="password"
|
||||
@keyup.enter="handleLogin(formRef)" :show-password="true"
|
||||
class="h-[40px] input-with-select">
|
||||
<template #prepend>
|
||||
<icon name="element-Lock" />
|
||||
</template>
|
||||
@ -29,7 +32,8 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" class="mt-[30px] h-[40px] w-full" @click="handleLogin(formRef)" :loading="loading">{{ loading ? t('logging') : t('login') }}</el-button>
|
||||
<el-button type="primary" class="mt-[30px] h-[40px] w-full" @click="handleLogin(formRef)"
|
||||
:loading="loading">{{ loading ? t('logging') : t('login') }}</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
@ -38,7 +42,8 @@
|
||||
</el-main>
|
||||
|
||||
<!-- 验证组件 -->
|
||||
<verify @success="success" :mode="pop" captchaType="blockPuzzle" :imgSize="{ width: '330px', height: '155px' }" ref="verifyRef"></verify>
|
||||
<verify @success="success" :mode="pop" captchaType="blockPuzzle" :imgSize="{ width: '330px', height: '155px' }"
|
||||
ref="verifyRef"></verify>
|
||||
<!-- <el-footer></el-footer> -->
|
||||
</el-container>
|
||||
</template>
|
||||
@ -67,6 +72,9 @@ const setFormData = async (id: number = 0) => {
|
||||
webSite.value = await (await getWebConfig()).data
|
||||
storage.set({ key: 'siteInfo', data: webSite.value })
|
||||
}
|
||||
const routerList = ref({
|
||||
tourism: "/tourism/index", vipcard: "/vipcard/index", cms: "/cms/article/list", shop: "/shop/hello_world"
|
||||
})
|
||||
setFormData()
|
||||
setWindowTitle(t('adminLogin'))
|
||||
|
||||
@ -117,8 +125,11 @@ const handleLogin = async (formEl: FormInstance | undefined) => {
|
||||
const loginFn = (data = {}) => {
|
||||
loading.value = true
|
||||
userStore.login({ username: form.username, password: form.password, ...data }).then(res => {
|
||||
const { query: { redirect } } = route
|
||||
const path = typeof redirect === 'string' ? redirect : '/'
|
||||
// const { query: { redirect } } = route
|
||||
// const path = typeof redirect === 'string' ? redirect : '/'
|
||||
let key = storage.get('menuAppStorage')
|
||||
if(!key) storage.set({key:'menuAppStorage',data:'tourism'})
|
||||
let path = key&&key!=''?routerList.value[key]:'/tourism/index'
|
||||
router.push(path)
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
@ -161,5 +172,4 @@ const loginFn = (data = {}) => {
|
||||
.login-main-left {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
}</style>
|
||||
|
||||
@ -15,53 +15,70 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('icon')" prop="icon">
|
||||
<div>
|
||||
<upload-image v-model="form.icon" />
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('iconPlaceholder1') }}</p>
|
||||
<upload-image v-model="form.icon" />
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('iconPlaceholder1') }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('key')" prop="key">
|
||||
<div>
|
||||
<el-input v-model="form.key" clearable :disabled="route.query.key"
|
||||
:placeholder="t('keyPlaceholder')" class="input-width mr-[15px]" />
|
||||
<el-button v-if="!route.query.key" type="primary" :disabled="form.key == ''"
|
||||
@click="getAddonDevelopCheckFn(form.key)">标识检测</el-button>
|
||||
@click="getAddonDevelopCheckFn(form.key)">官方市场标识检测</el-button>
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('keyPlaceholder1') }}</p>
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal">{{ t('keyPlaceholder2') }}</p>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('desc')" prop="desc">
|
||||
<el-input type="textarea" v-model="form.desc" clearable :placeholder="t('descPlaceholder')" class="input-width" />
|
||||
<el-input type="textarea" v-model="form.desc" clearable :placeholder="t('descPlaceholder')"
|
||||
class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('author')" prop="author">
|
||||
<el-input v-model="form.author" clearable :placeholder="t('authorPlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('version')" prop="version">
|
||||
<div>
|
||||
<el-input v-model="form.version" clearable :placeholder="t('versionPlaceholder')" class="input-width"
|
||||
onkeyup="this.value = this.value.replace(/[^\d\.]/g,'');" />
|
||||
<el-input v-model="form.version" clearable :placeholder="t('versionPlaceholder')"
|
||||
class="input-width" onkeyup="this.value = this.value.replace(/[^\d\.]/g,'');" />
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('versionPlaceholder1') }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('cover')" prop="cover">
|
||||
<div>
|
||||
<upload-image v-model="form.cover" />
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('coverPlaceholder1') }}</p>
|
||||
<upload-image v-model="form.cover" />
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('coverPlaceholder1') }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('type')" prop="type">
|
||||
<div>
|
||||
<el-select v-model="form.type" :placeholder="t('typePlaceholder')" class="input-width" clearable
|
||||
@change="typeChange">
|
||||
<el-option v-for="(item, key) in options" :key="key" :label="item" :value="key" />
|
||||
</el-select>
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('typePlaceholder1') }}</p>
|
||||
<el-select v-model="form.type" :placeholder="t('typePlaceholder')" class="input-width" clearable
|
||||
@change="typeChange">
|
||||
<el-option v-for="(item, key) in options" :key="key" :label="item" :value="key" />
|
||||
</el-select>
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">{{ t('typePlaceholder1') }}</p>
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal">{{ t('typePlaceholder2') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<template v-if="form.type === 'addon'">
|
||||
<el-form-item :label="t('supportType')">
|
||||
<div>
|
||||
<el-select v-model="form.support_type" class="input-width" @change="typeChange">
|
||||
<el-option label="通用插件" :value="1" />
|
||||
<el-option label="支持应用" :value="2" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('supportApp')" prop="support_app" v-if="form.support_type!=1">
|
||||
<el-select v-model="form.support_app" :placeholder="t('supportAppPlaceholder')" class="input-width">
|
||||
<el-option v-for="(item, index) in AppLst" :label="item.title" :value="item.key"
|
||||
:key="index" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<!-- <el-form-item v-if="form.type != 'app'" :label="t('supportApp')" prop="support_app">
|
||||
<el-input v-model="form.support_app" clearable :placeholder="t('supportAppPlaceholder')"
|
||||
class="input-width" />
|
||||
@ -79,8 +96,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getAddontype, addAddonDevelop, editAddonDevelop, getAddonDevelop, getAddonDevelopCheck } from '@/app/api/tools'
|
||||
import { ElMessageBox,ElMessage } from 'element-plus'
|
||||
import { getAddontype, addAddonDevelop, editAddonDevelop, getAddonDevelopInfo, getAddonDevelopCheck, getAddonDevelop } from '@/app/api/tools'
|
||||
import { getAddonList} from '@/app/api/sys'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@ -94,7 +112,8 @@ const form = ref({
|
||||
version: "",
|
||||
cover: "",
|
||||
type: "",
|
||||
support_app: ""
|
||||
support_app: "",
|
||||
support_type: 1
|
||||
})
|
||||
const options = ref([])
|
||||
const loading = ref(false)
|
||||
@ -147,25 +166,35 @@ const rules = ref({
|
||||
type: [
|
||||
{ required: true, message: t('typePlaceholder'), trigger: 'change' },
|
||||
],
|
||||
support_app: [
|
||||
{ required: true, message: t('typePlaceholder'), trigger: 'change' },
|
||||
],
|
||||
})
|
||||
onMounted(async () => {
|
||||
let res = await getAddontype()
|
||||
options.value = res.data
|
||||
if (route.query.key) getAddonDevelopFn(route.query.key)
|
||||
if (route.query.key) getAddonDevelopInfoFn(route.query.key)
|
||||
})
|
||||
const typeChange = () => {
|
||||
form.value.support_app = ''
|
||||
}
|
||||
//详情查询
|
||||
const getAddonDevelopFn = (key: any) => {
|
||||
const getAddonDevelopInfoFn = (key: any) => {
|
||||
loading.value = true
|
||||
getAddonDevelop(key).then(res => {
|
||||
getAddonDevelopInfo(key).then(res => {
|
||||
form.value = Object.assign(form.value, res.data)
|
||||
loading.value = false
|
||||
}).catch(()=>{
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
//获取app列表
|
||||
const AppLst = ref<Array<any>>([])
|
||||
const getAddonListFn = async () => {
|
||||
let { data } = await getAddonList({})
|
||||
AppLst.value = data
|
||||
}
|
||||
getAddonListFn()
|
||||
const getAddonDevelopCheckFn = (key: any) => {
|
||||
getAddonDevelopCheck(key).then(res => {
|
||||
ElMessageBox.alert(res.data ? t('warningText') : t('successText'), t('warning'), {
|
||||
@ -193,12 +222,12 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
message: t('onSaveSuccessText'),
|
||||
type: 'success',
|
||||
})
|
||||
|
||||
setTimeout(()=>{
|
||||
window.addonActiveName='pluginList'
|
||||
router.push({ path: "/tools/addon"})
|
||||
},650)
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
window.addonActiveName = 'pluginList'
|
||||
router.push({ path: "/tools/addon" })
|
||||
}, 650)
|
||||
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
// showDialog.value = false
|
||||
|
||||
@ -148,7 +148,7 @@
|
||||
<el-input v-model="params.search" :placeholder="t('titlePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getaddonDevelopFn">{{ t('search') }}</el-button>
|
||||
<el-button type="primary" @click="getAddonDevelopFn">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -200,7 +200,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive, toRefs, ref, onMounted } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getaddonDevelop, deleteAddonDevelop, addonDevelopBuild,addonDevelopDownload } from '@/app/api/tools'
|
||||
import { getAddonDevelop, deleteAddonDevelop, addonDevelopBuild,addonDevelopDownload } from '@/app/api/tools'
|
||||
import { img } from '@/utils/common'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
@ -228,11 +228,11 @@ onMounted(() => {
|
||||
state.activeName = window.addonActiveName + ''
|
||||
window.addonActiveName = null
|
||||
}
|
||||
getaddonDevelopFn()
|
||||
getAddonDevelopFn()
|
||||
})
|
||||
const getaddonDevelopFn = () => {
|
||||
const getAddonDevelopFn = () => {
|
||||
loading.value = true
|
||||
getaddonDevelop(state.params).then(res => {
|
||||
getAddonDevelop(state.params).then(res => {
|
||||
state.data = res.data
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
@ -244,7 +244,7 @@ const getaddonDevelopFn = () => {
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields();
|
||||
getaddonDevelopFn();
|
||||
getAddonDevelopFn();
|
||||
}
|
||||
const editEvent = (key: any) => {
|
||||
router.push({ path: '/tools/addon_edit', query: { key } })
|
||||
@ -296,7 +296,7 @@ const deleteEvent = (key: any) => {
|
||||
).then(() => {
|
||||
loading.value = true
|
||||
deleteAddonDevelop(key).then(() => {
|
||||
getaddonDevelopFn()
|
||||
getAddonDevelopFn()
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
import { ref, reactive, computed, toRaw, } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { getGeneratorAllModel, getGeneratorTableColumn,getaddonDevelop } from '@/app/api/tools'
|
||||
import { getGeneratorAllModel, getGeneratorTableColumn,getAddonDevelop } from '@/app/api/tools'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const showDialog = ref(false)
|
||||
@ -80,6 +80,9 @@ const formRules = computed(() => {
|
||||
name: [
|
||||
{ required: true, message: t('associatedNamePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
addon: [
|
||||
{ required: true, message: t('addonsPlaceholder'), trigger: 'change' }
|
||||
],
|
||||
model: [
|
||||
{ required: true, message: t('associatedModelPlaceholder'), trigger: 'change' }
|
||||
],
|
||||
@ -97,8 +100,8 @@ const formRules = computed(() => {
|
||||
* 获取关联模型
|
||||
*/
|
||||
const modelList = ref([])
|
||||
const getGeneratorAllModelFn = () => {
|
||||
getGeneratorAllModel().then(res => {
|
||||
const getGeneratorAllModelFn = (params:any) => {
|
||||
getGeneratorAllModel(params).then(res => {
|
||||
modelList.value = res.data
|
||||
})
|
||||
}
|
||||
@ -114,13 +117,13 @@ const getGeneratorTableColumnFn = (key: any) => {
|
||||
}
|
||||
//获取插件列表
|
||||
const addonLst = ref<Array<any>>([])
|
||||
const getaddonDevelopFn = async () => {
|
||||
let { data } = await getaddonDevelop({})
|
||||
const getAddonDevelopFn = async () => {
|
||||
let { data } = await getAddonDevelop({})
|
||||
addonLst.value = [{ title: "系统", key: "system" }]
|
||||
addonLst.value.push(...data)
|
||||
getGeneratorAllModelFn({addon:'system'})
|
||||
}
|
||||
getaddonDevelopFn()
|
||||
getAddonDevelopFn()
|
||||
//选择应用
|
||||
const addonChange =(val:any)=>{
|
||||
formData.value.model = ''
|
||||
|
||||
163
admin/src/app/views/tools/code/components/edit-verify.vue
Normal file
163
admin/src/app/views/tools/code/components/edit-verify.vue
Normal file
@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="title" width="480px" :before-close="beforeClose" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="130px" ref="formRef" :rules="formRules" class="page-form">
|
||||
|
||||
<el-form-item v-if="formData.validate_type == 'min'" :label="t('minLabel')" prop="min_number">
|
||||
<el-input-number v-model="formData.min_number" :step="1" step-strictly :min="1" class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item v-else-if="formData.validate_type == 'max'" :label="t('maxLabel')" prop="max_number">
|
||||
<el-input-number v-model="formData.max_number" :step="1" step-strictly :min="1" class="input-width" />
|
||||
</el-form-item>
|
||||
<template v-else-if="formData.view_type === 'number'">
|
||||
<el-form-item :label="t('minLabel1')" prop="view_min">
|
||||
<el-input-number v-model="formData.view_min" :min="0" :value-on-clear="0" class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('maxLabel1')" prop="view_max">
|
||||
<el-input-number v-model="formData.view_max" :min="1" class="input-width" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-form-item :label="t('minLabel')" prop="betweenMin">
|
||||
<el-input-number v-model="formData.betweenMin" :step="1" step-strictly :min="1" class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('maxLabel')" prop="betweenMax">
|
||||
<el-input-number v-model="formData.betweenMax" :step="1" step-strictly :min="1" class="input-width" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<!-- <el-form-item v-else :label="t('between')" required>
|
||||
<el-col :span="11">
|
||||
<el-form-item prop="betweenMin">
|
||||
<el-input-number v-model="formData.betweenMin" :step="1" step-strictly :min="1" class="input-width" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="2" class="text-center">
|
||||
<span class="text-gray-500">-</span>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<el-form-item prop="betweenMax">
|
||||
<el-input-number v-model="formData.betweenMax" :step="1" step-strictly :min="1" class="input-width" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup async>
|
||||
import { ref, reactive, computed, toRaw, } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const title = ref('')
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const initialFormData = {
|
||||
validate_type: "",
|
||||
min_number: 1,
|
||||
max_number: 120,
|
||||
betweenMin: 1,
|
||||
betweenMax: 120,
|
||||
}
|
||||
const formData: Record<string, any> = ref({ ...initialFormData })
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const validateMin = (rule: any, value: any, callback: any) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('minPlaceholder')))
|
||||
} else if (value > formData.value.betweenMax) {
|
||||
callback(new Error(t('minPlaceholder1')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateMax = (rule: any, value: any, callback: any) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('maxPlaceholder')))
|
||||
} else if (value < formData.value.betweenMin) {
|
||||
callback(new Error(t('maxPlaceholder1')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateMin1 = (rule: any, value: any, callback: any) => {
|
||||
if (value > formData.value.view_max) {
|
||||
callback(new Error(t('min1Placeholder1')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateMax1 = (rule: any, value: any, callback: any) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('max1Placeholder')))
|
||||
} else if (value < formData.value.view_min) {
|
||||
callback(new Error(t('max1Placeholder1')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
min_number: [
|
||||
{ required: true, message: t('minPlaceholder'), trigger: 'change' }
|
||||
],
|
||||
max_number: [
|
||||
{ required: true, message: t('maxPlaceholder'), trigger: 'change' }
|
||||
],
|
||||
betweenMin: [
|
||||
{ required: true, validator: validateMin, trigger: 'change' }
|
||||
],
|
||||
betweenMax: [
|
||||
{ required: true, validator: validateMax, trigger: 'change' }
|
||||
],
|
||||
view_min: [
|
||||
{ required: true, validator: validateMin1, trigger: 'change' }
|
||||
],
|
||||
view_max: [
|
||||
{ required: true, validator: validateMax1, trigger: 'change' }
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['complete'])
|
||||
/**
|
||||
* 确认
|
||||
* @param formEl
|
||||
*/
|
||||
const confirm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
emit('complete', toRaw(formData.value))
|
||||
showDialog.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setFormData = async (row: any = null) => {
|
||||
|
||||
formData.value = cloneDeep(Object.assign(initialFormData, row))
|
||||
showDialog.value = true
|
||||
}
|
||||
const beforeClose = (next: any) => {
|
||||
|
||||
formRef.value?.clearValidate()
|
||||
next()
|
||||
}
|
||||
defineExpose({
|
||||
showDialog,
|
||||
setFormData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
87
admin/src/app/views/tools/code/components/edit-view-type.vue
Normal file
87
admin/src/app/views/tools/code/components/edit-view-type.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="title" width="480px" :before-close="beforeClose" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="130px" ref="formRef" :rules="formRules" class="page-form">
|
||||
|
||||
<el-form-item :label="t('dictType')" >
|
||||
<el-select class="input-width" :placeholder="t('dictTypePlaceholder')" v-model="formData.dict_type"
|
||||
filterable remote clearable>
|
||||
<el-option :label="item.name" :value="item.key" v-for="item in dicList" :key="item.key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup async>
|
||||
import { ref, computed, toRaw, } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getDictAll } from '@/app/api/dict'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const title = ref('')
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const initialFormData = {
|
||||
dict_type: "",
|
||||
}
|
||||
const formData: Record<string, any> = ref({ ...initialFormData })
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const dicList = ref<Array<any>>([])
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
dict_type: [
|
||||
{ required: true, message: t('dictTypePlaceholder'), trigger: 'change' }
|
||||
],
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
const getDictAllFn = () => {
|
||||
getDictAll().then((res) => {
|
||||
dicList.value = res.data
|
||||
})
|
||||
}
|
||||
const emit = defineEmits(['complete'])
|
||||
/**
|
||||
* 确认
|
||||
* @param formEl
|
||||
*/
|
||||
const confirm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
emit('complete', toRaw(formData.value))
|
||||
showDialog.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setFormData = async (row: any = null) => {
|
||||
formData.value = cloneDeep(Object.assign(initialFormData, row))
|
||||
getDictAllFn()
|
||||
showDialog.value = true
|
||||
}
|
||||
const beforeClose = (next: any) => {
|
||||
|
||||
formRef.value?.clearValidate()
|
||||
next()
|
||||
}
|
||||
defineExpose({
|
||||
showDialog,
|
||||
setFormData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -21,8 +21,8 @@
|
||||
class="input-width" maxlength="64" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('addon')">
|
||||
<el-select class="input-width" :placeholder="t('addonPlaceholder')"
|
||||
v-model="formData.addon_name" filterable remote clearable :remote-method="getaddonDevelopFn"
|
||||
<el-select class="input-width" :placeholder="t('addonPlaceholder1')"
|
||||
v-model="formData.addon_name" filterable remote clearable :remote-method="getAddonDevelopFn"
|
||||
@change="addonChange">
|
||||
<el-option :label="item.title" :value="item.key" v-for="item in addonList"
|
||||
:key="item.key" />
|
||||
@ -56,39 +56,39 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('columnName')" prop="column_name" min-width="130px" />
|
||||
<el-table-column :label="t('columnComment')" prop="" min-width="230px">
|
||||
<el-table-column :label="t('columnComment')" prop="" min-width="220px">
|
||||
<template #default="{ row }">
|
||||
<el-input class="" v-model="row.column_comment"
|
||||
:placeholder="t('columnCommentPlaceholder')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('columnType')" prop="column_type" width="100px" />
|
||||
<el-table-column :label="t('isPk')" prop="" align="center" width="80px">
|
||||
<el-table-column :label="t('isPk')" prop="" align="center" width="65px">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.is_pk" disabled :true-label="1" :false-label="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('isRequired')" prop="" align="center" width="80px">
|
||||
<el-table-column :label="t('isRequired')" prop="" align="center" width="65px">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.is_required" :true-label="1" :false-label="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('isInsert')" prop="" align="center" width="80px">
|
||||
<el-table-column :label="t('isInsert')" prop="" align="center" width="65px">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.is_insert" :true-label="1" :false-label="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('isUpdate')" prop="" align="center" width="80px">
|
||||
<el-table-column :label="t('isUpdate')" prop="" align="center" width="65px">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.is_update" :true-label="1" :false-label="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('isLists')" prop="" align="center" width="80px">
|
||||
<el-table-column :label="t('isLists')" prop="" align="center" width="65px">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.is_lists" :true-label="1" :false-label="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('isSearch')" prop="" align="center" width="80px">
|
||||
<el-table-column :label="t('isSearch')" prop="" align="center" width="65px">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.is_search" :true-label="1" :false-label="0" />
|
||||
</template>
|
||||
@ -100,19 +100,51 @@
|
||||
</el-table-column> -->
|
||||
<el-table-column :label="t('queryType')" prop="" min-width="170px">
|
||||
<template #default="{ row }">
|
||||
<el-select v-if="row.is_search" :placeholder="t('selectPlaceholder')"
|
||||
v-model="row.query_type">
|
||||
<el-option :label="item" :value="item" v-for="(item, index) in queryType"
|
||||
:key="index" />
|
||||
</el-select>
|
||||
<div class="flex items-center">
|
||||
<el-select class="" v-if="row.is_search" :placeholder="t('selectPlaceholder')"
|
||||
v-model="row.query_type">
|
||||
<el-option :label="item" :value="item" v-for="(item, index) in queryType"
|
||||
:key="index" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('formType')" prop="" min-width="170px">
|
||||
<template #default="{ row }">
|
||||
<el-select :placeholder="t('selectPlaceholder')" v-model="row.view_type">
|
||||
<el-table-column :label="t('formType')" prop="" min-width="225px">
|
||||
<template #default="{ row, $index }">
|
||||
<el-select class="w-[146px]" :placeholder="t('selectPlaceholder')" v-model="row.view_type"
|
||||
@change="viewTypeBtn(row, $index)">
|
||||
<el-option :label="item.label" :value="item.value" v-for="(item, index) in viewType"
|
||||
:key="index" />
|
||||
</el-select>
|
||||
<el-button class="ml-[10px]" v-if="['select', 'radio', 'checkbox'].includes(row.view_type)"
|
||||
type="primary" link @click="viewTypeBtn(row, $index)">{{ t('setUp')
|
||||
}}</el-button>
|
||||
<el-button class="ml-[10px]" v-if="row.view_type === 'number'" type="primary" link
|
||||
@click="validatorBtn(row, $index)">{{ t('setUp')
|
||||
}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('verifyType')" prop="" min-width="260px">
|
||||
<template #default="{ row, $index }">
|
||||
<div class="flex items-center">
|
||||
<el-select class="w-[196px]" :placeholder="t('selectPlaceholder')"
|
||||
v-model="row.validate_type" @change="validatorBtn(row, $index)"
|
||||
:disabled="!['input', 'textarea'].includes(row.view_type)">
|
||||
<template v-for="(item, index) in verifyType" :key="index">
|
||||
<el-option v-if="item.value === 'max'" :value="item.value" :label="`最大输入字符`" />
|
||||
<el-option v-else-if="item.value === 'min'" :value="item.value"
|
||||
:label="`最小输入字符`" />
|
||||
<el-option v-else-if="item.value === 'between'" :value="item.value"
|
||||
:label="`输入字符区间`" />
|
||||
<el-option v-else :label="item.label" :value="item.value" />
|
||||
</template>
|
||||
</el-select>
|
||||
<el-button class="ml-[10px]"
|
||||
v-if="['max', 'min', 'between'].includes(row.validate_type)" type="primary" link
|
||||
@click="validatorBtn(row, $index)">{{ t('setUp')
|
||||
}}</el-button>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column :label="t('formValidation')" prop="" min-width="170px">
|
||||
@ -135,15 +167,22 @@
|
||||
</el-radio-group>
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">
|
||||
物理删除:从表中把记录移除。软删除:通过标识使得这条记录在系统逻辑层面上不可见</p>
|
||||
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item prop="delete_column_name" :label="t('deleteField')" v-if="formData.is_delete">
|
||||
<el-select class="input-width" :placeholder="t('deleteFieldPlaceholder')"
|
||||
v-model="formData.delete_column_name">
|
||||
<el-option :label="`${item.column_name}:${item.column_comment}`" :value="item.column_name"
|
||||
v-for="(item, index) in formData.table_column " :key="index" />
|
||||
</el-select>
|
||||
<div>
|
||||
<el-select class="input-width" :placeholder="t('deleteFieldPlaceholder')"
|
||||
v-model="formData.delete_column_name">
|
||||
<el-option :label="`${item.column_name}:${item.column_comment}`"
|
||||
:value="item.column_name" v-for="(item, index) in formData.table_column "
|
||||
:key="index" />
|
||||
</el-select>
|
||||
<p class="text-[12px] text-[#a9a9a9] leading-normal mt-[5px]">
|
||||
软删除字段需为int类型,并且默认值为0</p>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('editType')">
|
||||
@ -236,6 +275,8 @@
|
||||
</el-card>
|
||||
</div>
|
||||
<edit-associated ref="editDialog" :table_name="formData.table_name" @complete="complete" />
|
||||
<edit-view-type ref="editViewTypeRef" @complete="completeViewType" />
|
||||
<edit-verify ref="editVerifyRef" @complete="completeVerify" />
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" @click="onSave(1)">{{ t('save') }}</el-button>
|
||||
@ -252,7 +293,9 @@ import { t } from '@/lang'
|
||||
import { img } from '@/utils/common'
|
||||
import { FormInstance, ElMessageBox, ElMessage } from 'element-plus'
|
||||
import editAssociated from '@/app/views/tools/code/components/edit-associated.vue'
|
||||
import { getGenerateTableInfo, editGenerateTable, getaddonDevelop, generatorCheckFile, generateCreate } from '@/app/api/tools'
|
||||
import editViewType from '@/app/views/tools/code/components/edit-view-type.vue'
|
||||
import editVerify from '@/app/views/tools/code/components/edit-verify.vue'
|
||||
import { getGenerateTableInfo, editGenerateTable, getAddonDevelop, generatorCheckFile, generateCreate } from '@/app/api/tools'
|
||||
import { getSystemMenu, getAddonMenu } from '@/app/api/sys'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import Sortable from 'sortablejs'
|
||||
@ -314,13 +357,52 @@ const viewType = [
|
||||
{
|
||||
label: t('formEditor'),
|
||||
value: 'editor'
|
||||
},
|
||||
{
|
||||
label: t('formNumber'),
|
||||
value: 'number'
|
||||
}
|
||||
]
|
||||
const verifyType = [
|
||||
{
|
||||
label: '无需验证',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: t('mobileVerify'),
|
||||
value: 'mobile'
|
||||
},
|
||||
{
|
||||
label: t('numberVerify'),
|
||||
value: 'number'
|
||||
},
|
||||
{
|
||||
label: t('idCardVerify'),
|
||||
value: 'idCard'
|
||||
},
|
||||
{
|
||||
label: t('emailVerify'),
|
||||
value: 'email'
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
value: 'max'
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
value: 'min'
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
value: 'between'
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
const addonList = ref<Array<any>>([])
|
||||
//获取插件远程搜索
|
||||
const getaddonDevelopFn = (search: string) => {
|
||||
getaddonDevelop({ search }).then(res => {
|
||||
const getAddonDevelopFn = (search: string) => {
|
||||
getAddonDevelop({ search }).then(res => {
|
||||
addonList.value = res.data
|
||||
})
|
||||
}
|
||||
@ -345,7 +427,7 @@ const rowDrop = () => {
|
||||
}
|
||||
onMounted(() => {
|
||||
rowDrop()
|
||||
getaddonDevelopFn('')
|
||||
getAddonDevelopFn('')
|
||||
})
|
||||
//删除类型change
|
||||
const deleteTypeChange = (val: any) => {
|
||||
@ -377,6 +459,10 @@ const setFormData = async (id: number = 0) => {
|
||||
Object.keys(data).forEach((key: string) => {
|
||||
if (data[key] != undefined) formData[key] = data[key]
|
||||
})
|
||||
formData.table_column.forEach(el => {
|
||||
el.betweenMin = cloneDeep(el.min_number);
|
||||
el.betweenMax = cloneDeep(el.max_number);
|
||||
})
|
||||
if (formData.addon_name != '') getAddonMenuFn(formData.addon_name)
|
||||
loading.value = false
|
||||
}
|
||||
@ -409,6 +495,7 @@ const addonChange = async (val: any) => {
|
||||
const associatedIndex = ref(0)
|
||||
const editDialog = ref()
|
||||
//添加编辑关联设置
|
||||
|
||||
const addEvent = (val: any, index: number) => {
|
||||
associatedIndex.value = index
|
||||
editDialog.value.setFormData(val)
|
||||
@ -426,13 +513,28 @@ const deleteEvent = (index: number) => {
|
||||
formData.relations.splice(index, 1)
|
||||
}
|
||||
const onSave = async (code: number) => {
|
||||
loading.value = true
|
||||
|
||||
const data = cloneDeep(formData)
|
||||
// if (data.table_column.some(el => { return ['select', 'radio', 'checkbox'].includes(el.view_type) && el.dict_type == '' })) {
|
||||
// // ElMessage({
|
||||
// // type: 'error',
|
||||
// // message: t('dictTypePlaceholder'),
|
||||
// // })
|
||||
// // return false
|
||||
// }
|
||||
|
||||
data.table_column = JSON.stringify(data.table_column.map(el => {
|
||||
if (!el.is_search) el.query_type = ''
|
||||
if (el.validate_type === 'between' || el.view_type === 'number') {
|
||||
el.max_number = el.betweenMax
|
||||
el.min_number = el.betweenMin
|
||||
}
|
||||
if (!['select', 'radio', 'checkbox'].includes(el.view_type)) el.dict_type = ''
|
||||
return el
|
||||
}))
|
||||
console.log(JSON.parse(data.table_column))
|
||||
data.relations = JSON.stringify(data.relations)
|
||||
loading.value = true
|
||||
editGenerateTable(data).then((res: any) => {
|
||||
if (code === 3) {
|
||||
generatorCheckFileFn()
|
||||
@ -496,6 +598,37 @@ const generateCreateFn = (generate_type: any) => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const rowIndex = ref(0)
|
||||
const editVerifyRef = ref(null)
|
||||
const editViewTypeRef = ref(null)
|
||||
/**
|
||||
* 打开最大最小值设置
|
||||
*/
|
||||
|
||||
const validatorBtn = (row: any, index: number) => {
|
||||
if (['max', 'min', 'between'].includes(row.validate_type) || row.view_type === 'number') {
|
||||
rowIndex.value = index
|
||||
editVerifyRef.value?.setFormData(row)
|
||||
}
|
||||
|
||||
}
|
||||
const completeVerify = (row: any) => {
|
||||
formData.table_column.splice(rowIndex.value, 1, row)
|
||||
}
|
||||
const viewTypeBtn = (row: any, index: number) => {
|
||||
if (!['input', 'textarea'].includes(row.view_type)) row.validate_type = ''
|
||||
if (['select', 'radio', 'checkbox'].includes(row.view_type)) {
|
||||
|
||||
rowIndex.value = index
|
||||
editViewTypeRef.value?.setFormData(row)
|
||||
} else if (row.view_type === 'number') {
|
||||
validatorBtn(row, index)
|
||||
}
|
||||
|
||||
}
|
||||
const completeViewType = (row: any) => {
|
||||
formData.table_column.splice(rowIndex.value, 1, row)
|
||||
}
|
||||
const back = () => {
|
||||
router.push({ path: '/tools/code' })
|
||||
}
|
||||
|
||||
@ -97,14 +97,20 @@
|
||||
<el-tab-pane :label="t('codeList')" name="codeList">
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="codeTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('addonName')" prop="addon_name">
|
||||
<el-select v-model="codeTableData.searchParam.addon_name" placeholder="Select" filterable remote clearable :remote-method="getAddonDevelopFn">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="系统" value="2" />
|
||||
<el-option :label="item.title" :value="item.key" v-for="item in addonList"
|
||||
:key="item.key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('tableName')" prop="table_name">
|
||||
<el-input v-model="codeTableData.searchParam.table_name"
|
||||
:placeholder="t('tableNamePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('tableContent')" prop="table_content">
|
||||
<el-input v-model="codeTableData.searchParam.table_content"
|
||||
:placeholder="t('tableContentPlaceholder')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadGenerateTableList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
@ -120,6 +126,8 @@
|
||||
|
||||
<el-table-column prop="table_name" :show-overflow-tooltip="true" :label="t('tableName')"
|
||||
min-width="120" />
|
||||
<el-table-column prop="title" :show-overflow-tooltip="true" :label="t('addonName')"
|
||||
min-width="120" />
|
||||
<el-table-column prop="table_content" :show-overflow-tooltip="true" :label="t('tableContent')"
|
||||
min-width="120" />
|
||||
|
||||
@ -159,7 +167,7 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<add-table ref="addCodeDialog" />
|
||||
<el-dialog v-model="dialogVisible" width="70%" title="代码预览">
|
||||
<el-dialog v-model="dialogVisible" class="dialog-visible" width="70%" title="代码预览">
|
||||
<div class="flex h-[50vh]" v-loading="codeLoading">
|
||||
<el-scrollbar class="h-[100%] w-[270px]">
|
||||
<el-tree v-if="treeData.length && treeKey != ''" :data="treeData" :props="{ label: 'name', value: 'key' }"
|
||||
@ -182,7 +190,7 @@
|
||||
</el-scrollbar>
|
||||
<div class="ml-[20px]" style="width: calc(100% - 285px);">
|
||||
<el-scrollbar class="h-[100%] w-[100%]">
|
||||
<highlightjs autodetect :code="code" />
|
||||
<highlightjs autodetect class="h-[100%]" :code="code" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
@ -194,7 +202,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getGenerateTableList, deleteGenerateTable, generateCreate, generatePreview, generatorCheckFile, } from '@/app/api/tools'
|
||||
import { getGenerateTableList, deleteGenerateTable, generateCreate, generatePreview, generatorCheckFile,getAddonDevelop } from '@/app/api/tools'
|
||||
import { img } from '@/utils/common'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import AddTable from '@/app/views/tools/code/components/add-table.vue'
|
||||
@ -214,7 +222,8 @@ let codeTableData = reactive({
|
||||
data: [],
|
||||
searchParam: {
|
||||
table_name: "",
|
||||
table_content: ""
|
||||
table_content: "",
|
||||
addon_name:""
|
||||
}
|
||||
})
|
||||
|
||||
@ -253,8 +262,17 @@ const loadGenerateTableList = (page: number = 1) => {
|
||||
})
|
||||
}
|
||||
|
||||
const addonList = ref<Array<any>>([])
|
||||
//获取插件远程搜索
|
||||
const getAddonDevelopFn = (search: string) => {
|
||||
getAddonDevelop({ search }).then(res => {
|
||||
addonList.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const addCodeDialog: Record<string, any> | null = ref(null)
|
||||
|
||||
|
||||
/**
|
||||
* 添加代码生成
|
||||
*/
|
||||
@ -312,6 +330,7 @@ const generatorCheckFileFn = ((id: any) => {
|
||||
codeTableData.loading = false
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 同步or下载
|
||||
*/
|
||||
@ -333,6 +352,7 @@ const generateCreateFn = (id: any, generate_type: any) => {
|
||||
codeTableData.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
*代码预览
|
||||
*/
|
||||
@ -429,4 +449,11 @@ const listToTree = (arr) => {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}</style>
|
||||
}
|
||||
|
||||
:deep(.dialog-visible .el-scrollbar__view), :deep(.dialog-visible .el-scrollbar__view .hljs.ruby){
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
</style>
|
||||
|
||||
@ -118,5 +118,12 @@
|
||||
"indexSwitch": "切换首页",
|
||||
"indexWarning": "你确定要切换首页吗?",
|
||||
"appName": "应用名称",
|
||||
"appNamePlaceholder": "请输入应用名称"
|
||||
"appNamePlaceholder": "请输入应用名称",
|
||||
"generateMobile":"请输入正确的手机号格式",
|
||||
"generateNumber":"请输入整数",
|
||||
"generateIdCard":"请输入正确的身份证号",
|
||||
"generateEmail":"请输入正确的邮箱号",
|
||||
"generateMax":"超过最多输入字符数",
|
||||
"generateMin":"少于最少输入字符数",
|
||||
"generateBetween":"请输入正确的字符信息"
|
||||
}
|
||||
@ -1,76 +1,384 @@
|
||||
<template>
|
||||
<div :class="['flex',{'two-type': sidebar == 'twoType'},{'three-type': sidebar == 'threeType'}]">
|
||||
<div class="w-[72px] overflow-hidden">
|
||||
<el-aside :class="['h-screen layout-aside w-[93px] pr-[20px] pb-[30px] bg-[#F7F8FA] ease-in duration-200', { 'bright': !dark }]">
|
||||
<!-- 一级菜单 -->
|
||||
<div class="">
|
||||
<el-header class="logo-wrap h-auto">
|
||||
<div class="logo flex items-center m-auto max-w-[210px] h-[60px] justify-center">
|
||||
<!-- <img class="w-[35px] h-[35px] rounded-full" src="@/app/assets/images/login_logo_ico.png" alt=""> -->
|
||||
<span class="iconfont iconyun text-[#999] !text-[30px]"></span>
|
||||
</div>
|
||||
</el-header>
|
||||
<div :class="['flex', { 'two-type': sidebar == 'twoType' }, { 'three-type': sidebar == 'threeType' }]" v-if="aaa">
|
||||
|
||||
<div class="menu-wrap">
|
||||
<div class="w-[65px] overflow-hidden" v-if="!floatMenuStyle">
|
||||
<el-aside
|
||||
:class="['h-screen layout-aside w-[65px] pb-[30px] bg-[#F7F8FA] ease-in duration-200', { 'bright': !dark }]">
|
||||
<!-- 一级菜单 -->
|
||||
<div class="h-full flex flex-col pt-2 relative">
|
||||
<!-- <el-header class="logo-wrap h-auto mb-[10px]">
|
||||
<div class="logo flex items-center m-auto max-w-[210px] h-[60px] justify-center"#19233C>
|
||||
<span class="iconfont iconyun text-[#999] !text-[36px]"></span>
|
||||
</div>
|
||||
</el-header> -->
|
||||
|
||||
<!-- <template v-for="(item, index) in applyList" :key="index">
|
||||
<div v-if="item.type == 'app'" @click="appToLink(item.key)"
|
||||
class=" flex items-center justify-center h-[45px] mb-[5px] cursor-pointer text-[#6d7278] hover:bg-[#f1f2f6] menu-item hover:text-color whitespace-nowrap">
|
||||
<img :src="img(item.icon)" class="w-[35px] h-[35px] rounded-full" alt="" :title="item.title">
|
||||
</div>
|
||||
</template> -->
|
||||
<div class=" flex items-center justify-center h-[45px] mb-[5px] cursor-pointer cut-style" @click="floatActive=!floatActive">
|
||||
<span class="iconfont icontuodong !text-[30px] "></span>
|
||||
</div>
|
||||
<div class="mb-[20px]">
|
||||
<template v-for="(item, index) in menus" :key="index">
|
||||
<div v-if="item.meta.show" @click="toLink(item)"
|
||||
:class="['flex items-center justify-center h-[56px] cursor-pointer text-[#6d7278] hover:bg-[#f1f2f6] menu-item hover:text-color whitespace-nowrap', { 'bg-[#f1f2f6] text-color menu-item-active ': (item.path == matched.path || (matched.path == '/admin' && item.path == '/index') || (matched.meta.app && item.path == '/index')) }]">
|
||||
<icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto" size="24px" :title="item.meta.title" />
|
||||
<div v-if="item.meta.app == '' && item.meta.attr == 'common'" @click="toLink(item)"
|
||||
:class="['flex items-center justify-center h-[56px] cursor-pointer text-[#6d7278] hover:bg-[#f1f2f6] menu-item hover:text-color whitespace-nowrap', { 'bg-[#f1f2f6] text-color menu-item-active ': (item.path == currentRoute.path || (currentRoute.path == '/admin' && item.path == '/index') || (currentRoute.meta.app && item.path == '/index')) }]">
|
||||
<icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto" size="24px"
|
||||
:title="item.meta.title" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<a href="javascript:;"
|
||||
class="absolute -bottom-[20px] left-[50%] cut-style iconfont icongengduo !text-[30px] qx"
|
||||
@click="cutMenuStyleFn" title="切换"></a>
|
||||
</div>
|
||||
</el-aside>
|
||||
</div>
|
||||
<!-- 浮动样式的应用菜单 -->
|
||||
<div v-if="!floatMenuStyle&&floatActive"
|
||||
class="flex absolute bg-[#fff] w-[640px] px-[28px] py-[20px] flex-wrap left-0 top-[65px] z-10 box-border shadow-lg">
|
||||
<template v-for="(item, index) in applyList" :key="index">
|
||||
<div v-if="item.type == 'app'" @click="appToLink(item.key)"
|
||||
class="flex items-center cursor-pointer text-[#6d7278] hover:bg-[#f1f2f6] whitespace-nowrap py-[10px] px-[15px]">
|
||||
<img :src="img(item.icon)" class="w-[44px] h-[44px] rounded-full mr-[5px]" alt="" :title="item.title">
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 二级菜单 -->
|
||||
<template v-for="(item, index) in menus" :key="index">
|
||||
<div v-if="isTwoMenuFn(item)" class="w-[189px] box-border border-r-[1px] border-solid second-menu">
|
||||
<div
|
||||
class="group flex flex-col items-center justify-center h-[64px] border-b-[1px] border-solid second-head cursor-pointer relative">
|
||||
<div class="flex items-center">
|
||||
<template v-if="floatMenuStyle">
|
||||
<img v-if="appInfo.icon" :src="img(appInfo.icon)" class="w-[40px] h-[40px] mr-[8px]" alt="">
|
||||
<div class="flex items-center justify-center w-[30px] h-[30px]"
|
||||
v-else-if="Object.keys(appInfo).length">
|
||||
<icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto" size="24px" />
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ item.meta.app ? appInfo.title : item.meta.title }}</span>
|
||||
</div>
|
||||
<!-- 浮动样式的应用菜单 -->
|
||||
<div v-if="floatMenuStyle"
|
||||
class="hidden group-hover:flex absolute bg-[#fff] w-[640px] px-[28px] py-[20px] flex-wrap left-0 top-[65px] z-10 box-border shadow-lg">
|
||||
<template v-for="(item, index) in applyList" :key="index">
|
||||
<div v-if="item.type == 'app'" @click="appToLink(item.key)"
|
||||
class="flex items-center justify-center cursor-pointer text-[#6d7278] hover:bg-[#f1f2f6] whitespace-nowrap py-[10px] px-[15px]">
|
||||
<img :src="img(item.icon)" class="w-[44px] h-[44px] rounded-full mr-[5px]" alt=""
|
||||
:title="item.title">
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-aside>
|
||||
</div>
|
||||
|
||||
<!-- 二级菜单 -->
|
||||
<div v-if="matched.children.length" class="w-[189px] box-border border-r-[1px] border-solid second-menu">
|
||||
<div class="flex flex-col items-center justify-center h-[108px] border-b-[1px] border-solid second-head mx-[10px]">
|
||||
<div class="flex items-center justify-center w-[30px] h-[30px]" v-if="!matched.meta.app">
|
||||
<icon v-if="matched.meta.icon" :name="matched.meta.icon" class="!w-auto" size="24px" />
|
||||
</div>
|
||||
<img v-else-if="matched.meta.app && appInfo.icon" :src="img(appInfo.icon)" class="w-[40px] h-[40px]" alt="">
|
||||
<!-- <img v-else-if="matched.meta.app && !appInfo.icon" src="@/app/assets/images/login_logo_ico.png" class="w-[40px] h-[40px] rounded-full" alt=""> -->
|
||||
<div class="flex items-center">
|
||||
<span class=" mt-[2px]">{{ matched.meta.app ? appInfo.title : matched.meta.title }}</span>
|
||||
<span class="text-color ml-2 !text-[20px] cursor-pointer iconfont iconqiehuan2" v-if="matched.meta.app && appInfo.icon" @click="switchAppFn()"></span>
|
||||
</div>
|
||||
</div>
|
||||
<el-menu class="system-menu !border-0" :router="true" unique-opened="true" :default-active="String(route.name)">
|
||||
<template v-for="(twoMenu, twoIndex) in matched.children">
|
||||
<el-sub-menu :index="String(twoMenu.meta.title)" v-if="twoMenu.children && twoMenu.meta.show">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon" class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(threeMenu, threeIndex) in twoMenu.children" :key="threeIndex">
|
||||
<el-menu-item v-if="threeMenu.meta.show" class="!h-[52px] !pl-[64px]" :index="String(threeMenu.name)" @click="toLink(threeMenu)">
|
||||
<el-scrollbar class="overflow-y-auto menus-wrap">
|
||||
<el-menu class="apply-menu !border-0" :router="true" unique-opened="true"
|
||||
:default-active="String(route.name)">
|
||||
<template v-for="(twoMenu, twoIndex) in item.children">
|
||||
<el-sub-menu :index="String(twoMenu.meta.title)" v-if="twoMenu.children && twoMenu.meta.show">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ threeMenu.meta.title }}</span>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon" class="absolute !w-auto"
|
||||
size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(threeMenu, threeIndex) in twoMenu.children" :key="threeIndex">
|
||||
<!-- 三级菜单 -->
|
||||
<el-sub-menu :index="String(threeMenu.meta.title)" class="three-menu"
|
||||
v-if="threeMenu.children && threeMenu.meta.show">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="threeMenu.meta.icon && floatMenuStyle"
|
||||
:name="threeMenu.meta.icon" class="absolute !w-auto" size="18px" />
|
||||
<span v-if="!floatMenuStyle" class="iconfont icondian !text-[25px]"></span>
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ threeMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(fourMenu, fourIndex) in threeMenu.children" :key="fourIndex">
|
||||
<el-sub-menu :index="String(fourMenu.meta.title)"
|
||||
v-if="fourMenu.children && fourMenu.meta.show">
|
||||
<template #title>
|
||||
<div
|
||||
class="w-[16px] h-[16px] relative flex items-center justify-center">
|
||||
<span class="iconfont icondian !text-[25px]"></span>
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ fourMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(fiveMenu, fiveIndex) in fourMenu.children"
|
||||
:key="fiveIndex">
|
||||
<el-menu-item v-if="fiveMenu.meta.show" class="!h-[52px] !pl-[55px]"
|
||||
:index="String(fiveMenu.name)" @click="toLink(fiveMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ fiveMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="fourMenu.meta.show" class="!h-[52px] !pl-[35px]"
|
||||
:index="String(fourMenu.name)" @click="toLink(fourMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ fourMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
|
||||
<!-- 二级菜单 -->
|
||||
<el-menu-item v-else-if="threeMenu.meta.show" class="!h-[52px] !pl-[52px]"
|
||||
:index="String(threeMenu.name)" @click="toLink(threeMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ threeMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
</el-sub-menu>
|
||||
|
||||
<el-menu-item v-else-if="twoMenu.meta.show && twoMenu.meta.key != 'official_market'"
|
||||
class="!pl-[25px] text-[#333]" :index="String(twoMenu.name)" @click="toLink(twoMenu)">
|
||||
<template #title>
|
||||
<div v-if="twoMenu.meta.icon" class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon" class="absolute !w-auto"
|
||||
size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
|
||||
<el-menu-item v-else-if="twoMenu.meta.show && twoMenu.meta.key != 'official_market'" class="!pl-[35px] text-[#333]" :index="String(twoMenu.name)" @click="toLink(twoMenu)">
|
||||
<template #title>
|
||||
<div v-if="twoMenu.meta.icon" class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon" class="absolute !w-auto" size="18px" />
|
||||
<div class="flex items-center !px-[25px] h-[56px] cursor-pointer text-[#333] el-menu-item"
|
||||
v-else-if="twoMenu.meta.show && twoMenu.meta.key == 'official_market'"
|
||||
@click="toLink(twoMenu)">
|
||||
<div v-if="twoMenu.meta.icon" class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon" class="absolute !w-auto"
|
||||
size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<div class="flex items-center !px-[35px] h-[56px] cursor-pointer text-[#333] el-menu-item" v-else-if="twoMenu.meta.show && twoMenu.meta.key == 'official_market'" @click="toLink(twoMenu)">
|
||||
<div v-if="twoMenu.meta.icon" class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon" class="absolute !w-auto" size="18px"/>
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
<!-- 系统菜单 -->
|
||||
<template
|
||||
v-if="applyTypeList.includes(localMenuKey) || otherTypeList.includes(localMenuKey) || floatMenuStyle">
|
||||
<div class="!border-0 !border-t-[1px] border-solid mx-[25px] bg-[#f7f7f7] my-[5px]"></div>
|
||||
<template v-for="(twoMenu, twoIndex) in menus">
|
||||
<el-sub-menu :index="String(twoMenu.meta.title)"
|
||||
v-if="twoMenu.meta.attr == 'system' && !twoMenu.meta.app && twoMenu.children">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(threeMenu, threeIndex) in twoMenu.children" :key="threeIndex">
|
||||
<el-sub-menu :index="String(threeMenu.meta.title)"
|
||||
v-if="threeMenu.meta.app && threeMenu.children">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="threeMenu.meta.icon" :name="threeMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ threeMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(fourMenu, fourIndex) in threeMenu.children" :key="fourIndex">
|
||||
<!-- 三级菜单 -->
|
||||
<el-sub-menu :index="String(fourMenu.meta.title)"
|
||||
v-if="fourMenu.children && fourMenu.meta.show">
|
||||
<template #title>
|
||||
<div
|
||||
class="w-[16px] h-[16px] relative flex items-center justify-center">
|
||||
<span class="iconfont icondian !text-[25px]"></span>
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ fourMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(fiveMenu, fiveIndex) in fourMenu.children"
|
||||
:key="fiveIndex">
|
||||
<el-menu-item v-if="fiveMenu.meta.show" class="!h-[52px] !pl-[55px]"
|
||||
:index="String(fiveMenu.name)" @click="toLink(fiveMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ fiveMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="fourMenu.meta.show"
|
||||
class="!ml-[30px] !h-[52px] !pl-[35px]" :index="String(fourMenu.name)"
|
||||
@click="toLink(fourMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ fourMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-if="threeMenu.meta.show" class="!h-[52px] !pl-[52px]"
|
||||
:index="String(threeMenu.name)" @click="toLink(threeMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ threeMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
<!-- 插件菜单 -->
|
||||
<template v-if="otherTypeList.includes(localMenuKey) && plugMenuType">
|
||||
<template v-for="(twoMenu, twoIndex) in menus">
|
||||
<el-sub-menu :index="String(twoMenu.meta.title)"
|
||||
v-if="twoMenu.meta.app && twoMenu.meta.app == plugMenuType && twoMenu.children">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(threeMenu, threeIndex) in twoMenu.children"
|
||||
:key="threeIndex">
|
||||
<!-- 三级菜单 -->
|
||||
<el-sub-menu :index="String(threeMenu.meta.title)"
|
||||
v-if="threeMenu.children && threeMenu.meta.show">
|
||||
<template #title>
|
||||
<div
|
||||
class="w-[16px] h-[16px] relative flex items-center justify-center">
|
||||
<span class="iconfont icondian !text-[25px]"></span>
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ threeMenu.meta.title
|
||||
}}</span>
|
||||
</template>
|
||||
<template v-for="(fourMenu, fourIndex) in threeMenu.children"
|
||||
:key="fourIndex">
|
||||
<el-menu-item v-if="fourMenu.meta.show"
|
||||
class="!h-[52px] !pl-[55px]" :index="String(fourMenu.name)"
|
||||
@click="toLink(fourMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ fourMenu.meta.title
|
||||
}}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="threeMenu.meta.show"
|
||||
class="!ml-[30px] !h-[52px] !pl-[35px]"
|
||||
:index="String(threeMenu.name)" @click="toLink(threeMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ threeMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="twoMenu.meta.app && twoMenu.meta.app == plugMenuType"
|
||||
class="!pl-[25px] text-[#333]" :index="String(twoMenu.name)"
|
||||
@click="toLink(twoMenu)">
|
||||
<template #title>
|
||||
<div v-if="twoMenu.meta.icon"
|
||||
class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="twoMenu.meta.attr == 'system' && !twoMenu.meta.app"
|
||||
class="!pl-[25px] text-[#333]" :index="String(twoMenu.name)" @click="toLink(twoMenu)">
|
||||
<template #title>
|
||||
<div v-if="twoMenu.meta.icon" class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 浮动样式 -->
|
||||
<template v-if="floatMenuStyle">
|
||||
<div class="!border-0 !border-t-[1px] border-solid mx-[25px] bg-[#f7f7f7] my-[5px]"></div>
|
||||
<template v-for="(twoMenu, twoIndex) in menus">
|
||||
<el-sub-menu :index="String(twoMenu.meta.title)"
|
||||
v-if="twoMenu.meta.attr == 'common' && !twoMenu.meta.app && twoMenu.children">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(threeMenu, threeIndex) in twoMenu.children" :key="threeIndex">
|
||||
<el-sub-menu :index="String(threeMenu.meta.title)"
|
||||
v-if="threeMenu.children && threeMenu.meta.show">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="threeMenu.meta.icon" :name="threeMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ threeMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(fourMenu, fourIndex) in threeMenu.children" :key="fourIndex">
|
||||
<el-sub-menu :index="String(fourMenu.meta.title)"
|
||||
v-if="fourMenu.children && fourMenu.meta.show">
|
||||
<template #title>
|
||||
<div
|
||||
class="w-[16px] h-[16px] relative flex items-center justify-center">
|
||||
<span class="iconfont icondian !text-[25px]"></span>
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ fourMenu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="(fiveMenu, fiveIndex) in threeMenu.children"
|
||||
:key="fiveIndex">
|
||||
<el-menu-item v-if="fiveMenu.meta.show" class="!h-[52px] !pl-[55px]"
|
||||
:index="String(fiveMenu.name)" @click="toLink(fiveMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ fiveMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="fourMenu.meta.show" class="!h-[52px] !pl-[55px]"
|
||||
:index="String(fourMenu.name)" @click="toLink(fourMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ fourMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
|
||||
<el-menu-item
|
||||
v-else-if="threeMenu.meta.show && threeMenu.meta.key != 'official_market'"
|
||||
class="!h-[52px] !pl-[52px]" :index="String(threeMenu.name)"
|
||||
@click="toLink(threeMenu)">
|
||||
<template #title>
|
||||
<span class="text-[14px]">{{ threeMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<div class="flex items-center !px-[52px] h-[56px] cursor-pointer text-[#333] el-menu-item"
|
||||
v-else-if="threeMenu.meta.show && threeMenu.meta.key == 'official_market'"
|
||||
@click="toLink(threeMenu)">
|
||||
<span class="text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="twoMenu.meta.attr == 'common'" class="!pl-[35px] text-[#333]"
|
||||
:index="String(twoMenu.name)" @click="toLink(twoMenu)">
|
||||
<template #title>
|
||||
<div v-if="twoMenu.meta.icon" class="w-[16px] h-[16px] relative flex items-center">
|
||||
<icon v-if="twoMenu.meta.icon" :name="twoMenu.meta.icon"
|
||||
class="absolute !w-auto" size="18px" />
|
||||
</div>
|
||||
<span class="ml-[11px] text-[15px]">{{ twoMenu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -85,47 +393,124 @@ import storage from '@/utils/storage'
|
||||
import { CollectionTag } from '@element-plus/icons-vue'
|
||||
import { findFirstValidRoute } from '@/router/routers'
|
||||
import { getAddonByKey } from '@/app/api/addon'
|
||||
import { getApply } from '@/app/api/apply'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const systemStore = useSystemStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
let globalAppKey = ref('') //菜单类型
|
||||
let localMenuKey = ref('') //菜单类型
|
||||
globalAppKey.value = storage.get('menuAppStorage')
|
||||
localMenuKey.value = storage.get('menuAppStorage')
|
||||
|
||||
// 应用跳转 start
|
||||
let applyList = ref([]);
|
||||
let applyTypeList = ref([]);
|
||||
let otherTypeList = ref([]); // 存着插件\会员\应用管理
|
||||
let aaa = ref(false)
|
||||
const getApplelist = async () => {
|
||||
const res = await getApply()
|
||||
applyList.value = applyList.value.concat(res.data)
|
||||
applyList.value.forEach((item, index) => {
|
||||
if (item.type == 'app')
|
||||
applyTypeList.value.push(item.key)
|
||||
if (item.type == 'addon')
|
||||
otherTypeList.value.push(item.key)
|
||||
})
|
||||
otherTypeList.value = otherTypeList.value.concat(['member', 'overview'])
|
||||
aaa.value = true;
|
||||
}
|
||||
getApplelist()
|
||||
const floatActive = ref(false)
|
||||
const appLink = ref({})
|
||||
const getAppLink = () => {
|
||||
userStore.routers.forEach((item, index) => {
|
||||
if (item.meta.app != '') {
|
||||
if (item.children && item.children.length) {
|
||||
appLink.value[item.meta.app] = findFirstValidRoute(item.children)
|
||||
} else {
|
||||
appLink.value[item.meta.app] = item.name
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
getAppLink()
|
||||
const appToLink = (addon: string) => {
|
||||
globalAppKey.value = addon;
|
||||
localMenuKey.value = addon;
|
||||
|
||||
storage.set({ key: 'menuAppStorage', data: addon })
|
||||
storage.set({ key: 'plugMenuTypeStorage', data: '' })
|
||||
|
||||
let data = userStore.appMenuList
|
||||
if (!data.length) {
|
||||
data.push(addon)
|
||||
} else if (!data.includes(addon)) {
|
||||
data.push(addon)
|
||||
}
|
||||
|
||||
userStore.setAppMenuList(data)
|
||||
floatActive.value = false
|
||||
router.push({ name: appLink.value[addon] })
|
||||
|
||||
}
|
||||
// 应用跳转 end
|
||||
|
||||
|
||||
const menus = computed(() => {
|
||||
const menus = []
|
||||
userStore.routers.forEach((item, index) => {
|
||||
if (item.meta.app == '') {
|
||||
if (item.children && item.children.length) {
|
||||
item.name = findFirstValidRoute(item.children)
|
||||
menus.push(item)
|
||||
} else {
|
||||
menus.push(item)
|
||||
}
|
||||
if (item.children && item.children.length) {
|
||||
item.name = findFirstValidRoute(item.children)
|
||||
menus.push(item)
|
||||
} else {
|
||||
menus.push(item)
|
||||
}
|
||||
})
|
||||
return menus
|
||||
})
|
||||
|
||||
let currMetaAppType = "";
|
||||
const matched = computed(() => {
|
||||
const data = route.matched[1]
|
||||
if (data.meta.app && (!currMetaAppType || currMetaAppType != data.meta.app)){
|
||||
appInfo.value = {};
|
||||
currMetaAppType = data.meta.app;
|
||||
getAppInfo(data.meta.app)
|
||||
}
|
||||
return route.matched[1]
|
||||
})
|
||||
|
||||
const dark = computed(() => {
|
||||
return systemStore.dark
|
||||
})
|
||||
|
||||
// 获取应用详情
|
||||
const appInfo = ref({})
|
||||
const getAppInfo = (type) => {
|
||||
getAddonByKey(type).then(res => {
|
||||
appInfo.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
let currMetaAppType = "";
|
||||
let plugMenuType = ref(''); //插件类型
|
||||
let currentRoute = ref(''); // 当前路由
|
||||
watch(route, () => {
|
||||
plugMenuType.value = storage.get('plugMenuTypeStorage')
|
||||
|
||||
let data = route.matched[1]
|
||||
currentRoute.value = route.matched[1];
|
||||
localMenuKey.value = data.meta.key;
|
||||
|
||||
data.meta.app = !data.meta.app && !data.meta.attr ? 'member' : data.meta.app;
|
||||
|
||||
// 等待applist加载完成
|
||||
setTimeout(() => {
|
||||
// 获取应用详情
|
||||
if (data.meta.app && (!currMetaAppType || currMetaAppType != data.meta.app)) {
|
||||
appInfo.value = {};
|
||||
currMetaAppType = data.meta.app;
|
||||
let appInfoKey = otherTypeList.value.includes(data.meta.app) ? globalAppKey.value : data.meta.app;
|
||||
getAppInfo(appInfoKey)
|
||||
}
|
||||
}, 800);
|
||||
|
||||
systemStore.$patch(state => {
|
||||
state.menuDrawer = false
|
||||
})
|
||||
})
|
||||
}, { immediate: true })
|
||||
|
||||
|
||||
// 跳转链接
|
||||
const toLink = (data) => {
|
||||
@ -136,28 +521,34 @@ const toLink = (data) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 切换应用
|
||||
const switchAppFn = () => {
|
||||
router.push({ path: '/index/index' })
|
||||
}
|
||||
|
||||
// 获取应用详情
|
||||
const appInfo = ref({})
|
||||
const getAppInfo = (type) => {
|
||||
getAddonByKey(type).then(res => {
|
||||
appInfo.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
// 主题风格
|
||||
const sidebar = computed(() => {
|
||||
return systemStore.sidebar
|
||||
})
|
||||
|
||||
// 切换风格
|
||||
let floatMenuStyle = ref(false);
|
||||
floatMenuStyle.value = storage.get('floatMenuStyle') || false;
|
||||
const cutMenuStyleFn = () => {
|
||||
floatMenuStyle.value = true;
|
||||
storage.set({ key: 'floatMenuStyle', data: true });
|
||||
location.reload();
|
||||
}
|
||||
|
||||
|
||||
// 控制二级菜单的显示
|
||||
const isTwoMenuFn = (item) => {
|
||||
let bool = (otherTypeList.value.includes(localMenuKey.value) && globalAppKey.value == item.meta.app)
|
||||
|| (floatMenuStyle.value && !applyTypeList.value.includes(localMenuKey.value) && !otherTypeList.value.includes(localMenuKey.value) && globalAppKey.value == item.meta.app)
|
||||
|| (floatMenuStyle.value && applyTypeList.value.includes(localMenuKey.value) && (item.meta.key == localMenuKey.value || item.meta.app == localMenuKey.value))
|
||||
|| (!floatMenuStyle.value && !otherTypeList.value.includes(localMenuKey.value) && (item.meta.key == localMenuKey.value || item.meta.app == localMenuKey.value))
|
||||
return bool;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.layout-aside {
|
||||
border-right: 1px solid var(--el-border-color-lighter);
|
||||
// border-right: 1px solid var(--el-border-color-lighter);
|
||||
|
||||
&.bright {
|
||||
background-color: #F5F7F9;
|
||||
@ -188,15 +579,21 @@ const sidebar = computed(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.second-menu .el-sub-menu .el-sub-menu__title{
|
||||
padding-left: 35px !important;
|
||||
.el-icon.el-sub-menu__icon-arrow{
|
||||
right: 35px;
|
||||
.second-menu .el-sub-menu .el-sub-menu__title {
|
||||
padding-left: 25px !important;
|
||||
padding-right: 25px !important;
|
||||
|
||||
.el-icon.el-sub-menu__icon-arrow {
|
||||
right: 25px;
|
||||
font-weight: bolder;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.three-menu.el-sub-menu .el-sub-menu__title {
|
||||
padding-left: 45px !important;
|
||||
}
|
||||
|
||||
.text-color {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
@ -207,10 +604,17 @@ const sidebar = computed(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.cut-style {
|
||||
color: #6d7278;
|
||||
|
||||
}
|
||||
.cut-style.qx {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
// 主题二
|
||||
.two-type{
|
||||
.logo-wrap{
|
||||
.logo span{
|
||||
.two-type {
|
||||
.logo-wrap {
|
||||
.logo span {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
@ -222,40 +626,54 @@ const sidebar = computed(() => {
|
||||
font-size: 25px !important;
|
||||
}
|
||||
}
|
||||
.layout-aside{
|
||||
background-color: #12192D;
|
||||
.menu-item{
|
||||
|
||||
.layout-aside {
|
||||
// background-color: #12192D;
|
||||
background-color: #2b303b;
|
||||
|
||||
.menu-item {
|
||||
color: #fff;
|
||||
&.menu-item-active, &:hover{
|
||||
|
||||
&.menu-item-active,
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.second-menu{
|
||||
|
||||
.second-menu {
|
||||
background-color: #F5F7F9;
|
||||
.el-menu{
|
||||
|
||||
.el-menu {
|
||||
background-color: transparent;
|
||||
|
||||
.el-menu-item:hover {
|
||||
background-color: #fff;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-menu-item.is-active {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
.el-sub-menu__title:hover{
|
||||
|
||||
.el-sub-menu__title:hover {
|
||||
background-color: #fff;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.cut-style {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 主题三
|
||||
.three-type{
|
||||
.logo-wrap{
|
||||
.logo span{
|
||||
.three-type {
|
||||
.logo-wrap {
|
||||
.logo span {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
@ -267,38 +685,58 @@ const sidebar = computed(() => {
|
||||
font-size: 25px !important;
|
||||
}
|
||||
}
|
||||
.layout-aside{
|
||||
background-color: #12192D;
|
||||
.menu-item{
|
||||
|
||||
.layout-aside {
|
||||
background-color: #2b303b;
|
||||
|
||||
.menu-item {
|
||||
color: #fff;
|
||||
&.menu-item-active, &:hover{
|
||||
background-color: #19233C;
|
||||
|
||||
&.menu-item-active,
|
||||
&:hover {
|
||||
background-color: #303848;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.second-menu{
|
||||
background-color: #19233C;
|
||||
.second-head{
|
||||
|
||||
.second-menu {
|
||||
background-color: #303848;
|
||||
|
||||
.second-head {
|
||||
color: #fff;
|
||||
border-color: #364059;
|
||||
}
|
||||
.el-menu{
|
||||
|
||||
.el-menu {
|
||||
background-color: transparent;
|
||||
.el-menu-item{
|
||||
|
||||
.el-menu-item {
|
||||
color: #fff;
|
||||
}
|
||||
.el-menu-item:hover, .el-menu-item.is-active {
|
||||
|
||||
.el-menu-item:hover,
|
||||
.el-menu-item.is-active {
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.el-sub-menu__title:hover{
|
||||
background-color: var(--el-color-primary);;
|
||||
|
||||
.el-sub-menu__title:hover {
|
||||
background-color: var(--el-color-primary);
|
||||
;
|
||||
}
|
||||
.el-sub-menu__title{
|
||||
|
||||
.el-sub-menu__title {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.cut-style {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
.menus-wrap {
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,6 +3,10 @@
|
||||
<el-row class="w-100 h-full w-full">
|
||||
<el-col :span="12">
|
||||
<div class="left-panel h-full flex items-center">
|
||||
<!-- 刷新当前页 -->
|
||||
<div v-if="floatMenuStyle" class="navbar-item flex items-center h-full cursor-pointer" @click="cutMenuStyleFn(false)">
|
||||
<a href="javascript:;" title="切换" class="iconfont iconqiehuan2"></a>
|
||||
</div>
|
||||
<!-- 刷新当前页 -->
|
||||
<div class="navbar-item flex items-center h-full cursor-pointer" @click="refreshRouter">
|
||||
<icon name="element-Refresh" />
|
||||
@ -172,6 +176,15 @@ const submitIndex = () => {
|
||||
router.go(0)
|
||||
})
|
||||
}
|
||||
|
||||
// 切换风格
|
||||
let floatMenuStyle = ref(false);
|
||||
floatMenuStyle.value = storage.get('floatMenuStyle') || false;
|
||||
const cutMenuStyleFn = (bool)=>{
|
||||
floatMenuStyle.value = bool;
|
||||
storage.set({ key: 'floatMenuStyle', data: bool });
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -77,7 +77,8 @@ interface Route {
|
||||
},
|
||||
children?: [],
|
||||
is_show: boolean,
|
||||
addon: string
|
||||
addon: string,
|
||||
menu_attr: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,9 +87,10 @@ interface Route {
|
||||
* @param parentRoute
|
||||
*/
|
||||
const createRoute = function (route: Route, parentRoute: RouteRecordRaw | null = null): RouteRecordRaw {
|
||||
// parentRoute ? Symbol(`${parentRoute.path}/${route.router_path}`) : Symbol(`/${route.router_path}`)
|
||||
const record: RouteRecordRaw = {
|
||||
path: parentRoute ? route.router_path : `/${route.router_path}`,
|
||||
name: parentRoute ? Symbol(`${parentRoute.path}/${route.router_path}`) : Symbol(`/${route.router_path}`),
|
||||
name: route.menu_key,
|
||||
meta: {
|
||||
title: route.menu_name,
|
||||
icon: route.icon,
|
||||
@ -96,7 +98,8 @@ const createRoute = function (route: Route, parentRoute: RouteRecordRaw | null =
|
||||
show: route.is_show,
|
||||
app: route.addon,
|
||||
view: route.view_path,
|
||||
key: route.menu_key
|
||||
key: route.menu_key,
|
||||
attr: route.menu_attr
|
||||
}
|
||||
}
|
||||
if (route.menu_type == 0) {
|
||||
|
||||
@ -227,6 +227,7 @@ html.dark {
|
||||
|
||||
// /* 多行超出隐藏 */
|
||||
.multi-hidden {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3883393 */
|
||||
src: url('//at.alicdn.com/t/c/font_3883393_tcu9x1pe11k.woff2?t=1693640799122') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_tcu9x1pe11k.woff?t=1693640799122') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_tcu9x1pe11k.ttf?t=1693640799122') format('truetype');
|
||||
src: url('//at.alicdn.com/t/c/font_3883393_236yfsl6lh5.woff2?t=1694750730982') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_236yfsl6lh5.woff?t=1694750730982') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_236yfsl6lh5.ttf?t=1694750730982') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icondian:before {
|
||||
content: "\ec1e";
|
||||
}
|
||||
|
||||
.iconjiantou_xiangzuoliangci_o:before {
|
||||
content: "\eb93";
|
||||
}
|
||||
|
||||
.iconfenlei:before {
|
||||
content: "\e6c3";
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ class Request {
|
||||
baseURL: import.meta.env.VITE_APP_BASE_URL.substr(-1) == '/' ? import.meta.env.VITE_APP_BASE_URL : `${import.meta.env.VITE_APP_BASE_URL}/`,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;',
|
||||
'Content-Type': 'application/json;',
|
||||
'lang': storage.get('lang') ?? 'zh-cn'
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user