mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2025-12-10 17:22:47 +00:00
0.2.1
This commit is contained in:
parent
ed7ae681bf
commit
9bc5f0f3bd
@ -1,7 +1,7 @@
|
||||
NODE_ENV = 'development'
|
||||
|
||||
# api请求地址
|
||||
VITE_APP_BASE_URL='/adminapi/'
|
||||
VITE_APP_BASE_URL=''
|
||||
|
||||
# 图片服务器地址
|
||||
VITE_IMG_DOMAIN=''
|
||||
|
||||
1
admin/components.d.ts
vendored
1
admin/components.d.ts
vendored
@ -51,6 +51,7 @@ declare module '@vue/runtime-core' {
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElResult: typeof import('element-plus/es')['ElResult']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
|
||||
@ -19,6 +19,8 @@ const route = useRoute()
|
||||
// 初始化设置语言
|
||||
const systemStore = useSystemStore()
|
||||
const locale = computed(() => (systemStore.lang === 'zh-cn' ? zhCn : en))
|
||||
// 查询website信息
|
||||
systemStore.getWebsiteInfo()
|
||||
|
||||
const toggleDark = useToggle(useDark())
|
||||
|
||||
|
||||
@ -10,6 +10,14 @@ import request from '@/utils/request'
|
||||
export function getDiyPageList(params: Record<string, any>) {
|
||||
return request.get(`diy/diy`, {params})
|
||||
}
|
||||
/**
|
||||
* 获取自定义页面分页列表,轮播搜索组件用
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getDiyPageListByCarouselSearch(params: Record<string, any>) {
|
||||
return request.get(`diy/carousel_search`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义页面列表
|
||||
|
||||
@ -46,7 +46,7 @@ export function getModuleVersion() {
|
||||
* @returns
|
||||
*/
|
||||
export function downloadVersion(params: Record<string, any>) {
|
||||
return request.post(`addon/download/${params.addon}`, params, { showSuccessMessage: true })
|
||||
return request.post(`addon/download/${params.addon}`, params, { timeout: 0, showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -159,7 +159,7 @@ export function getWebsite() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网站设置
|
||||
* 获取网站设置 (未登录状态调用)
|
||||
* @returns
|
||||
*/
|
||||
export function getWebConfig() {
|
||||
|
||||
@ -59,6 +59,83 @@ export function getBatchAcquisition(params: Record<string, any>) {
|
||||
return request.put('wechat/template/sync', params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询关键字回复列表
|
||||
*/
|
||||
export function getKeywordsReplyList(params: Record<string, any>) {
|
||||
return request.get('wechat/reply/keywords', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改关键字回复
|
||||
*/
|
||||
export function editKeywordsReply(params: Record<string, any>) {
|
||||
return request.put(`wechat/reply/keywords/${params.id}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改关键字回复
|
||||
*/
|
||||
export function addKeywordsReply(params: Record<string, any>) {
|
||||
return request.post('wechat/reply/keywords', params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询关键字回复信息
|
||||
* @param id
|
||||
*/
|
||||
export function getKeywordsReplyInfo(id: number) {
|
||||
return request.get(`wechat/reply/keywords/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改关键字回复
|
||||
*/
|
||||
export function delKeywordsReply(id: number) {
|
||||
return request.delete(`wechat/reply/keywords/${id}`, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认回复
|
||||
*/
|
||||
export function getDefaultReply() {
|
||||
return request.get('wechat/reply/default')
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认回复
|
||||
* @param params
|
||||
*/
|
||||
export function setDefaultReply(params: Record<string, any>) {
|
||||
return request.put('wechat/reply/default', params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关注回复
|
||||
*/
|
||||
export function getSubscribeReply() {
|
||||
return request.get('wechat/reply/subscribe')
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置关注回复
|
||||
*/
|
||||
export function setSubscribeReply(params: Record<string, any>) {
|
||||
return request.put('wechat/reply/subscribe', params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询素材列表
|
||||
*/
|
||||
export function getMediaList(params: Record<string, any>) {
|
||||
return request.get('wechat/media', { params })
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步素材库
|
||||
*/
|
||||
export function syncNews() {
|
||||
return request.get('wechat/sync/news')
|
||||
}
|
||||
|
||||
BIN
admin/src/app/assets/images/logo.png
Normal file
BIN
admin/src/app/assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
81
admin/src/app/components/user-info-edit/index.vue
Normal file
81
admin/src/app/components/user-info-edit/index.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="t('accountSettings')"
|
||||
width="500"
|
||||
>
|
||||
<el-form :model="saveInfo" label-width="90px" ref="formRef" class="page-form">
|
||||
<el-form-item :label="t('headImg')">
|
||||
<upload-image v-model="saveInfo.head_img" :limit="1" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('userName')">
|
||||
<span>{{saveInfo.username}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('realName')">
|
||||
<el-input v-model="saveInfo.real_name" :placeholder="t('realNamePlaceholder')" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="flex justify-end">
|
||||
<el-button type="primary" @click="submitForm(formRef)" :loading="loading">{{ t('save') }}</el-button>
|
||||
<el-button @click="dialogVisible = false">{{ t('cancel') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
|
||||
import { getUserInfo, setUserInfo } from '@/app/api/personal'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
// 提交信息
|
||||
const saveInfo = reactive({})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(true)
|
||||
const dialogVisible = ref(false)
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
const getUserInfoFn = () => {
|
||||
|
||||
getUserInfo().then(res => {
|
||||
loading.value = false
|
||||
const data = res.data
|
||||
saveInfo.head_img = data.head_img
|
||||
saveInfo.real_name = data.real_name
|
||||
saveInfo.username = data.username
|
||||
})
|
||||
}
|
||||
getUserInfoFn()
|
||||
const open = ()=>{
|
||||
dialogVisible.value = true
|
||||
getUserInfoFn()
|
||||
}
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
setUserInfo(saveInfo).then((res: any) => {
|
||||
loading.value = false
|
||||
dialogVisible.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@ -6,6 +6,9 @@
|
||||
"menuTypeMenu": "菜单",
|
||||
"menuTypeButton": "按钮",
|
||||
"menuDeleteTips": "删除菜单会删除当前菜单以及该菜单下所有子菜单,是否确认删除?",
|
||||
"initializeMenu":"重置菜单",
|
||||
"initializeMenuTipsOne":"重置菜单会将应用或插件的dict目录下的菜单配置文件中,菜单配置更新到数据库一般用做开发者修改了dict菜单配置文件后,同步到数据库操作。",
|
||||
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常不允诛进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
|
||||
"addMenu": "添加菜单",
|
||||
"updateMenu": "编辑菜单",
|
||||
"routePath": "路由路径",
|
||||
|
||||
@ -7,6 +7,9 @@
|
||||
"menuTypeButton": "按钮",
|
||||
"menuDeleteTips": "确定要删除该菜单吗?",
|
||||
"addMenu": "添加菜单",
|
||||
"initializeMenu":"重置菜单",
|
||||
"initializeMenuTipsOne":"重置菜单会将应用或插件的dict目录下的菜单配置文件中,菜单配置更新到数据库一般用做开发者修改了dict菜单配置文件后,同步到数据库操作。",
|
||||
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常不允诛进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
|
||||
"updateMenu": "编辑菜单",
|
||||
"routePath": "路由路径",
|
||||
"viewPath": "组件路径",
|
||||
|
||||
@ -20,4 +20,4 @@
|
||||
"subscribeMessage": "订阅消息",
|
||||
"weappRelease": "版本管理",
|
||||
"alert":"您正在体验通用版小程序,不发布你将不可用"
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,5 +15,6 @@
|
||||
"customMenu": "自定义菜单",
|
||||
"wechatAccessBtn": "查看审核进度",
|
||||
"clickAccess2":"扫描二维码进入微信公众号",
|
||||
"wechatTemplate": "模板消息"
|
||||
}
|
||||
"wechatTemplate": "模板消息",
|
||||
"reply": "自动回复"
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
{
|
||||
"ruleName": "规则名称",
|
||||
"ruleNameTips": "规则名不能为空且最多60个字",
|
||||
"ruleNamePlaceholder": "请输入规则名称",
|
||||
"keywordPlaceholder": "请输入关键字",
|
||||
"contentPlaceholder": "请选择回复内容",
|
||||
"allMatching": "全匹配",
|
||||
"fuzzyMatching": "模糊匹配",
|
||||
"keyword": "关键字",
|
||||
"content": "回复内容",
|
||||
"replyMethod": "回复方式",
|
||||
"replyMethodAll": "全部回复",
|
||||
"replyMethodRand": "随机回复一条",
|
||||
"addReplyContent": "添加回复内容"
|
||||
}
|
||||
@ -19,5 +19,6 @@
|
||||
"wechatAccessFlow": "接入流程",
|
||||
"customMenu": "自定义菜单",
|
||||
"wechatTemplate": "模板消息",
|
||||
"menusEmptyTips": "空菜单,不能保存与发布。"
|
||||
}
|
||||
"menusEmptyTips": "空菜单,不能保存与发布。",
|
||||
"reply": "自动回复"
|
||||
}
|
||||
|
||||
12
admin/src/app/lang/zh-cn/channel.wechat.reply.json
Normal file
12
admin/src/app/lang/zh-cn/channel.wechat.reply.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"title": "自动回复",
|
||||
"wechatAccessFlow": "接入流程",
|
||||
"wechatSet": "公众号配置",
|
||||
"customMenu": "自定义菜单",
|
||||
"wechatTemplate": "模板消息",
|
||||
"reply": "自动回复",
|
||||
"keywordReply": "关键词回复",
|
||||
"defaultReply": "默认回复",
|
||||
"subscribeReply": "关注回复",
|
||||
"replyDeleteTips": "确定要删除该条回复吗?"
|
||||
}
|
||||
@ -21,5 +21,5 @@
|
||||
"wechatAccessFlow": "接入流程",
|
||||
"customMenu": "自定义菜单",
|
||||
"wechatTemplate": "模板消息",
|
||||
"id": "id"
|
||||
}
|
||||
"reply": "自动回复"
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"copyrightEdit":"版权设置",
|
||||
"copyrightEdit":"基础设置",
|
||||
"putOnRecordEdit":"备案设置",
|
||||
|
||||
"companyName": "公司名称",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"buyerNotice": "会员消息",
|
||||
"sellerNotice":"平台消息",
|
||||
"sellerNotice":"商家消息",
|
||||
"sms":"短信",
|
||||
"weapp":"微信小程序",
|
||||
"wechat":"微信公众号",
|
||||
@ -18,5 +18,6 @@
|
||||
"weappTempKey" : "模板编号",
|
||||
"smsId":"短信模版ID",
|
||||
"smsIdPlaceholder":"短信模版ID",
|
||||
"noticeType":"消息类型"
|
||||
}
|
||||
"noticeType":"消息类型",
|
||||
"addon": "所属应用"
|
||||
}
|
||||
|
||||
@ -25,14 +25,10 @@
|
||||
<span class="text-[14px] text-[#666]">{{ authinfo.company_name || '--' }}</span>
|
||||
</div>
|
||||
<div class="mt-[46px] ml-[40px] flex flex-wrap">
|
||||
<span class="text-[14px] mr-[84px]">授权域名<em
|
||||
class="ml-[12px] text-[12px]">{{ authinfo.site_address || '--'
|
||||
}}</em></span>
|
||||
<span class="text-[14px] mr-[84px]">授权域名<em class="ml-[12px] text-[12px]">{{ authinfo.site_address || '--' }}</em></span>
|
||||
<span class="text-[14px] flex items-center">
|
||||
<span>授权码</span>
|
||||
<em class="ml-[12px] mr-[10px] text-[12px]">{{ authinfo.auth_code ? (isCheck
|
||||
?
|
||||
authinfo.auth_code : hideAuthCode(authinfo.auth_code)) : '--' }}</em>
|
||||
<em class="ml-[12px] mr-[10px] text-[12px]">{{ authinfo.auth_code ? (isCheck ? authinfo.auth_code : hideAuthCode(authinfo.auth_code)) : '--' }}</em>
|
||||
<el-icon v-if="!isCheck" @click="isCheck = !isCheck" class="text-[12px] cursor-pointer">
|
||||
<View />
|
||||
</el-icon>
|
||||
@ -43,8 +39,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 flex-wrap justify-end relative">
|
||||
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary"
|
||||
@click="authCodeApproveFn">授权码认证</el-button>
|
||||
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
|
||||
<el-popover ref="getAuthCodeDialog" placement="bottom-start" :width="478" trigger="click"
|
||||
class="mt-[8px]">
|
||||
<div class="px-[18px] py-[8px]">
|
||||
@ -52,8 +47,7 @@
|
||||
</p>
|
||||
<div class="flex justify-end mt-[36px]">
|
||||
<el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
|
||||
<el-button class="w-[100px] !h-[48px]" plain
|
||||
@click="getAuthCodeDialog.hide()">关闭</el-button>
|
||||
<el-button class="w-[100px] !h-[48px]" plain @click="getAuthCodeDialog.hide()">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
@ -67,14 +61,12 @@
|
||||
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-form-item prop="auth_code">
|
||||
<el-input v-model="formData.auth_code" :placeholder="t('authCodePlaceholder')"
|
||||
class="input-width" clearable size="large" />
|
||||
<el-input v-model="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width" clearable size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<div class="mt-[20px]">
|
||||
<el-form-item prop="auth_secret">
|
||||
<el-input v-model="formData.auth_secret" clearable
|
||||
:placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||
<el-input v-model="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
@ -82,8 +74,7 @@
|
||||
|
||||
<div class="mt-[20px]">
|
||||
<el-button type="primary" class="w-full" size="large" :loading="saveLoading"
|
||||
@click="save(formRef)">{{
|
||||
t('confirm') }}</el-button>
|
||||
@click="save(formRef)">{{ t('confirm') }}</el-button>
|
||||
</div>
|
||||
<div class="mt-[10px] text-right">
|
||||
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
|
||||
@ -201,15 +192,13 @@ const save = async (formEl: FormInstance | undefined) => {
|
||||
if (valid) {
|
||||
saveLoading.value = true
|
||||
|
||||
setAuthinfo(formData)
|
||||
.then(() => {
|
||||
saveLoading.value = false
|
||||
checkAppMange()
|
||||
})
|
||||
.catch(() => {
|
||||
saveLoading.value = false
|
||||
authCodeApproveDialog.value = false
|
||||
})
|
||||
setAuthinfo(formData).then(() => {
|
||||
saveLoading.value = false
|
||||
checkAppMange()
|
||||
}).catch(() => {
|
||||
saveLoading.value = false
|
||||
authCodeApproveDialog.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -6,15 +6,13 @@
|
||||
</div>
|
||||
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="appList.length">
|
||||
<div v-for="(item, index) in appList" :key="index + 'b'">
|
||||
<div
|
||||
class="relative app-item cursor-pointer px-4 mr-4 mt-[20px] bg-[#f7f7f7] border-[1px] hover:border-primary">
|
||||
<div 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" />
|
||||
<img class="w-[50px] h-[50px]" src="@/app/assets/images/index/app_default.png" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
|
||||
@ -1,216 +1,274 @@
|
||||
<template>
|
||||
<div class="pt-[64px] px-[90px] app-store" v-loading="loading">
|
||||
<div v-if="info[activeName] && !loading">
|
||||
<div class="flex justify-between items-center h-[32px] mb-4">
|
||||
<span class="text-[26px] text-[#222] font-600">{{ t('localAppText') }}</span>
|
||||
<div class="w-[247px]">
|
||||
<el-input :placeholder="t('search')" v-model="searchName" @keyup.enter="query">
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon cursor-pointer" size="14px" @click="query">
|
||||
<search />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-[24px]">
|
||||
<div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'installed' }"
|
||||
class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
|
||||
@click="activeName = 'installed'">
|
||||
{{ t('installLabel') }}
|
||||
</div>
|
||||
<div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'uninstalled' }"
|
||||
class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
|
||||
@click="activeName = 'uninstalled'">
|
||||
{{ t('uninstalledLabel') }}
|
||||
</div>
|
||||
<div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'all' }"
|
||||
class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
|
||||
@click="activeName = 'all'">
|
||||
{{ t('buyLabel') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[32px]">
|
||||
<el-table v-if="localList[activeName].length" :data="info[activeName]" size="large" class="pt-[5px]">
|
||||
<el-table-column :label="t('appName')" align="left" width="320">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center"
|
||||
:class="{ 'cursor-pointer': row.type == 'app' && Object.keys(row.install_info).length }"
|
||||
@click="itemPath(row)">
|
||||
<el-image class="w-[54px] h-[54px]" :src="row.icon" fit="contain">
|
||||
<template #error>
|
||||
<img class="w-[54px] h-[54px]" src="@/app/assets/images/icon-addon.png" alt="">
|
||||
</template>
|
||||
</el-image>
|
||||
<div
|
||||
class="flex flex-col justify-center h-[54px] pl-[20px] text-[#222] font-500 text-[13px]">
|
||||
<div class="w-[236px] truncate leading-[18px]">{{ row.title }}</div>
|
||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]">{{ row.version }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" min-width="120">
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<span class="text-[#222] font-500 text-[13px] mr-[5px]">{{ t('appIdentification') }}</span>
|
||||
<el-tooltip class="box-item" effect="light" :content="t('tipText')" placement="bottom">
|
||||
<el-icon class="cursor-pointer text-[16px] text-[#a9a9a9]">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px]">{{ row.key }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="" :label="t('introduction')" align="left" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('type')" align="left" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px]">{{ row.type === 'app' ? t('app') : t('addon')
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="" :label="t('author')" align="left" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px]">{{ row.author }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-button class="!text-[13px]" type="primary" link @click="getAddonDetialFn(row)">{{
|
||||
t('detail') }}</el-button>
|
||||
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length"
|
||||
type="primary" link @click="uninstallAddonFn(row.key)">{{ t('unload') }}</el-button>
|
||||
|
||||
<el-button class="!text-[13px]" v-else-if="row.is_download && row.install_info <= 0"
|
||||
type="primary" link @click="installAddonFn(row.key)">{{ t('install')
|
||||
}}</el-button>
|
||||
<el-button class="!text-[13px]" v-else :loading="downloading == row.key"
|
||||
:disabled="downloading != ''" type="primary" link @click.stop="downEvent(row)">{{
|
||||
t('down') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty class="mx-auto overview-empty"
|
||||
v-if="!localList.installed.length && !loading && activeName == 'installed'">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">{{ t('installed-empty') }}</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<el-empty class="mx-auto overview-empty"
|
||||
v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled'">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">
|
||||
<span>{{ t('descriptionLeft') }}</span>
|
||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
|
||||
<span>{{ t('descriptionRight') }}</span>
|
||||
</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<div v-if="!localList.all.length && !loading && !authinfo && activeName == 'all'"
|
||||
class="mx-auto overview-empty flex flex-col items-center pt-14 pb-6">
|
||||
<div class="mb-[20px] text-sm text-[#888]">检测到当前账号尚未绑定授权,请先绑定授权!</div>
|
||||
<div class="flex flex-1 flex-wrap justify-center relative">
|
||||
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary"
|
||||
@click="authCodeApproveFn">授权码认证</el-button>
|
||||
<el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click"
|
||||
class="mt-[8px]">
|
||||
<div class="px-[18px] py-[8px]">
|
||||
<p class="leading-[32px] text-[14px]">
|
||||
您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
||||
<div class="flex justify-end mt-[36px]">
|
||||
<el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
|
||||
<el-button class="w-[100px] !h-[48px]" plain
|
||||
@click="getAuthCodeDialog.hide()">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="w-[154px] !h-[48px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent"
|
||||
plain type="primary">如何获取授权码?</el-button>
|
||||
<div class="app-store" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div v-if="info[activeName] && !loading">
|
||||
<div class="flex justify-between items-center h-[32px]">
|
||||
<span class="text-[16px] text-[#222] font-600">{{ t('localAppText') }}</span>
|
||||
<div class="w-[247px]">
|
||||
<el-input :placeholder="t('search')" v-model="searchName" @keyup.enter="query">
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon cursor-pointer" size="14px" @click="query">
|
||||
<search />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
|
||||
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-form-item prop="auth_code">
|
||||
<el-input v-model="formData.auth_code" :placeholder="t('authCodePlaceholder')"
|
||||
class="input-width" clearable size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<div class="mt-[20px]">
|
||||
<el-form-item prop="auth_secret">
|
||||
<el-input v-model="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')"
|
||||
class="input-width" size="large" />
|
||||
<div class="flex mt-[16px]">
|
||||
<div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'installed' }"
|
||||
class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
|
||||
@click="activeName = 'installed'">
|
||||
{{ t('installLabel') }}
|
||||
</div>
|
||||
<div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'uninstalled' }"
|
||||
class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
|
||||
@click="activeName = 'uninstalled'">
|
||||
{{ t('uninstalledLabel') }}
|
||||
</div>
|
||||
<div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'all' }"
|
||||
class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
|
||||
@click="activeName = 'all'">
|
||||
{{ t('buyLabel') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[32px]">
|
||||
<el-table v-if="localList[activeName].length" :data="info[activeName]" size="large" class="pt-[5px]">
|
||||
<el-table-column :label="t('appName')" align="left" width="320">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center"
|
||||
:class="{ 'cursor-pointer': row.type == 'app' && Object.keys(row.install_info).length }"
|
||||
@click="itemPath(row)">
|
||||
<el-image class="w-[54px] h-[54px]" :src="row.icon" fit="contain">
|
||||
<template #error>
|
||||
<img class="w-[54px] h-[54px]" src="@/app/assets/images/icon-addon.png" alt="">
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="flex flex-col justify-center h-[54px] pl-[20px] text-[#222] font-500 text-[13px]">
|
||||
<div class="w-[236px] truncate leading-[18px]">{{ row.title }}</div>
|
||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]">{{ row.version }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" min-width="120">
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<span class="text-[#222] font-500 text-[13px] mr-[5px]">{{ t('appIdentification') }}</span>
|
||||
<el-tooltip class="box-item" effect="light" :content="t('tipText')" placement="bottom">
|
||||
<el-icon class="cursor-pointer text-[16px] text-[#a9a9a9]">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px]">{{ row.key }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="" :label="t('introduction')" align="left" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('type')" align="left" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px]">{{ row.type === 'app' ? t('app') : t('addon') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="" :label="t('author')" align="left" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<span class="text-[#222] font-500 text-[13px]">{{ row.author }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-button class="!text-[13px]" type="primary" link @click="getAddonDetialFn(row)">{{ t('detail') }}</el-button>
|
||||
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length" type="primary" link @click="uninstallAddonFn(row.key)">{{ t('unload') }}</el-button>
|
||||
<el-button class="!text-[13px]" v-else-if="row.is_download && row.install_info <= 0" type="primary" link @click="installAddonFn(row.key)">{{ t('install') }}</el-button>
|
||||
<el-button class="!text-[13px]" v-else :loading="downloading == row.key" :disabled="downloading != ''" type="primary" link @click.stop="downEvent(row)">{{ t('down') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading && activeName == 'installed'">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">{{ t('installed-empty') }}</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled'">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">
|
||||
<span>{{ t('descriptionLeft') }}</span>
|
||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
|
||||
<span>{{ t('descriptionRight') }}</span>
|
||||
</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<div v-if="!localList.all.length && !loading && !authinfo && activeName == 'all'" class="mx-auto overview-empty flex flex-col items-center pt-14 pb-6">
|
||||
<div class="mb-[20px] text-sm text-[#888]">检测到当前账号尚未绑定授权,请先绑定授权!</div>
|
||||
<div class="flex flex-1 flex-wrap justify-center relative">
|
||||
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
|
||||
<el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click" class="mt-[8px]">
|
||||
<div class="px-[18px] py-[8px]">
|
||||
<p class="leading-[32px] text-[14px]">
|
||||
您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
||||
<div class="flex justify-end mt-[36px]">
|
||||
<el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
|
||||
<el-button class="w-[100px] !h-[48px]" plain @click="getAuthCodeDialog.hide()">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="w-[154px] !h-[48px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent"
|
||||
plain type="primary">如何获取授权码?</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
|
||||
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-form-item prop="auth_code">
|
||||
<el-input v-model="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width" clearable size="large" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="text-sm mt-[10px] text-info">{{ t('authInfoTips') }}</div>
|
||||
<div class="mt-[20px]">
|
||||
<el-form-item prop="auth_secret">
|
||||
<el-input v-model="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="mt-[20px]">
|
||||
<el-button type="primary" class="w-full" size="large" :loading="saveLoading"
|
||||
@click="save(formRef)">{{ t('confirm') }}</el-button>
|
||||
</div>
|
||||
<div class="mt-[10px] text-right">
|
||||
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
<!-- 详情 -->
|
||||
<el-dialog v-model="appStoreShowDialog" :title="t('plugDetail')" width="500px" :destroy-on-close="true">
|
||||
<el-form :model="appStoreInfo" label-width="120px" ref="formRef" class="page-form">
|
||||
<el-form-item :label="t('title')">
|
||||
<div class="input-width"> {{ appStoreInfo.title }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('desc')">
|
||||
<div class="input-width"> {{ appStoreInfo.desc }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('author')">
|
||||
<div class="input-width"> {{ appStoreInfo.author }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('version')">
|
||||
<div class="input-width"> {{ appStoreInfo.version }} </div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="appStoreShowDialog = false">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<div class="text-sm mt-[10px] text-info">{{ t('authInfoTips') }}</div>
|
||||
|
||||
<!-- 安装弹窗 -->
|
||||
<el-dialog v-model="installShowDialog" :title="t('addonInstall')" width="850px" :close-on-click-modal="false"
|
||||
:close-on-press-escape="false" :before-close="installShowDialogClose">
|
||||
<el-steps :space="200" :active="installStep" finish-status="success" align-center>
|
||||
<el-step :title="t('envCheck')" class="flex-1" />
|
||||
<el-step :title="t('installProgress')" class="flex-1" />
|
||||
<el-step :title="t('installComplete')" class="flex-1" />
|
||||
</el-steps>
|
||||
<div v-show="installStep == 1" v-loading="!installCheckResult.dir">
|
||||
<div class="mt-[20px]">
|
||||
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t('confirm') }}</el-button>
|
||||
</div>
|
||||
<div class="mt-[10px] text-right">
|
||||
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
<!-- 详情 -->
|
||||
<el-dialog v-model="appStoreShowDialog" :title="t('plugDetail')" width="500px" :destroy-on-close="true">
|
||||
<el-form :model="appStoreInfo" label-width="120px" ref="formRef" class="page-form">
|
||||
<el-form-item :label="t('title')">
|
||||
<div class="input-width"> {{ appStoreInfo.title }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('desc')">
|
||||
<div class="input-width"> {{ appStoreInfo.desc }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('author')">
|
||||
<div class="input-width"> {{ appStoreInfo.author }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('version')">
|
||||
<div class="input-width"> {{ appStoreInfo.version }} </div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="appStoreShowDialog = false">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 安装弹窗 -->
|
||||
<el-dialog v-model="installShowDialog" :title="t('addonInstall')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :before-close="installShowDialogClose">
|
||||
<el-steps :space="200" :active="installStep" finish-status="success" align-center>
|
||||
<el-step :title="t('envCheck')" class="flex-1" />
|
||||
<el-step :title="t('installProgress')" class="flex-1" />
|
||||
<el-step :title="t('installComplete')" class="flex-1" />
|
||||
</el-steps>
|
||||
<div v-show="installStep == 1" v-loading="!installCheckResult.dir">
|
||||
<el-scrollbar max-height="50vh">
|
||||
<div class="min-h-[150px]">
|
||||
<div class="bg-[#fff] my-3" v-if="installCheckResult.dir">
|
||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
||||
<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>
|
||||
</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]" v-for="(item, index) in installCheckResult.dir.is_readable" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('readable') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in installCheckResult.dir.is_write" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('write') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><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-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>
|
||||
<div v-show="installStep == 2" class="h-[50vh] mt-[20px]">
|
||||
<terminal name="my-terminal" :context="currAddon" :init-log="null" :show-header="false" :show-log-time="true" />
|
||||
</div>
|
||||
<div v-show="installStep == 3" class="h-[50vh] mt-[20px] flex flex-col">
|
||||
<el-result icon="success" :title="t('addonInstallSuccess')"></el-result>
|
||||
<!-- 提示信息 -->
|
||||
<div v-for="(item, index) in installAfterTips" class="mb-[10px]" :key="index">
|
||||
<el-alert :title="item" type="error" :closable="false" />
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="uninstallShowDialog" :title="t('addonUninstall')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-scrollbar max-height="50vh">
|
||||
<div class="min-h-[150px]">
|
||||
<div class="bg-[#fff] my-3" v-if="installCheckResult.dir">
|
||||
<div class="bg-[#fff] my-3" v-if="uninstallCheckResult.dir">
|
||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
||||
<div class="px-[20px] pt-[10px] text-[14px]">
|
||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||
@ -224,8 +282,7 @@
|
||||
<span>{{ t('status') }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]"
|
||||
v-for="(item, index) in installCheckResult.dir.is_readable" :key="index">
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in uninstallCheckResult.dir.is_readable" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
@ -241,8 +298,7 @@
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]"
|
||||
v-for="(item, index) in installCheckResult.dir.is_write" :key="index">
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in uninstallCheckResult.dir.is_write" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
@ -262,92 +318,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="flex justify-end">
|
||||
<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>
|
||||
<div v-show="installStep == 2" class="h-[50vh] mt-[20px]">
|
||||
<terminal name="my-terminal" :context="currAddon" :init-log="null" :show-header="false"
|
||||
:show-log-time="true" />
|
||||
</div>
|
||||
<div v-show="installStep == 3" class="h-[50vh] mt-[20px] flex flex-col">
|
||||
<el-result icon="success" :title="t('addonInstallSuccess')"></el-result>
|
||||
<!-- 提示信息 -->
|
||||
<div v-for="(item, index) in installAfterTips" class="mb-[10px]" :key="index">
|
||||
<el-alert :title="item" type="error" :closable="false" />
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="uninstallShowDialog" :title="t('addonUninstall')" width="850px"
|
||||
:close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-scrollbar max-height="50vh">
|
||||
<div class="min-h-[150px]">
|
||||
<div class="bg-[#fff] my-3" v-if="uninstallCheckResult.dir">
|
||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
||||
<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>
|
||||
</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]"
|
||||
v-for="(item, index) in uninstallCheckResult.dir.is_readable" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('readable') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]"
|
||||
v-for="(item, index) in uninstallCheckResult.dir.is_write" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('write') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><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>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -74,8 +74,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('menuShortName')">
|
||||
<el-input v-model="formData.menu_short_name" :placeholder="t('menuShortNamePlaceholder')"
|
||||
class="input-width" />
|
||||
<el-input v-model="formData.menu_short_name" :placeholder="t('menuShortNamePlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('sort')">
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="popTitle" width="500px" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form"
|
||||
v-loading="loading">
|
||||
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
|
||||
<el-form-item :label="t('accountNumber')" v-if="!formData.uid" prop="uid">
|
||||
<el-select :model-value="uid" :placeholder="t('accountNumberPlaceholder')" class="input-width" filterable
|
||||
clearable :allow-create="true" @change="selectUser" :default-first-option="true">
|
||||
<el-select :model-value="uid" :placeholder="t('accountNumberPlaceholder')" class="input-width" filterable clearable :allow-create="true" @change="selectUser" :default-first-option="true">
|
||||
<el-option v-for="item in userList" :key="item.uid" :label="item.username" :value="item.uid">
|
||||
<div class="flex items-center">
|
||||
<el-avatar :src="img(item.head_img)" size="small" class="mr-[10px]" v-if="item.head_img" />
|
||||
@ -17,8 +15,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('accountNumber')" prop="username" v-else>
|
||||
<el-input v-model="formData.username" :placeholder="t('accountNumberPlaceholder')" clearable
|
||||
:disabled="formData.uid" class="input-width" maxlength="10" show-word-limit />
|
||||
<el-input v-model="formData.username" :placeholder="t('accountNumberPlaceholder')" clearable :disabled="formData.uid" class="input-width" maxlength="10" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="needAddUserInfo">
|
||||
@ -27,26 +24,22 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('userRealName')" prop="real_name">
|
||||
<el-input v-model="formData.real_name" :placeholder="t('userRealNamePlaceholder')" clearable
|
||||
class="input-width" maxlength="10" show-word-limit />
|
||||
<el-input v-model="formData.real_name" :placeholder="t('userRealNamePlaceholder')" clearable class="input-width" maxlength="10" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="!formData.uid">
|
||||
<el-form-item :label="t('password')" prop="password">
|
||||
<el-input v-model="formData.password" :placeholder="t('passwordPlaceholder')" type="password"
|
||||
:show-password="true" clearable class="input-width" />
|
||||
<el-input v-model="formData.password" :placeholder="t('passwordPlaceholder')" type="password" :show-password="true" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('confirmPassword')" prop="confirm_password">
|
||||
<el-input v-model="formData.confirm_password" :placeholder="t('confirmPasswordPlaceholder')"
|
||||
type="password" :show-password="true" clearable class="input-width" />
|
||||
<el-input v-model="formData.confirm_password" :placeholder="t('confirmPasswordPlaceholder')" type="password" :show-password="true" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form-item :label="t('userRoleName')" prop="role_ids" v-if="!formData.userrole.is_admin">
|
||||
<el-select v-model="formData.role_ids" :placeholder="t('userRolePlaceholder')" class="input-width" multiple
|
||||
collapse-tags collapse-tags-tooltip>
|
||||
<el-select v-model="formData.role_ids" :placeholder="t('userRolePlaceholder')" class="input-width" multiple collapse-tags collapse-tags-tooltip>
|
||||
<el-option :label="item.role_name" :value="item.role_id" v-for="(item, index) in roles" :key="index" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@ -2,6 +2,9 @@
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center mt-[16px]">
|
||||
<el-form :inline="true" :model="sysUserLogTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('ip')" prop="ip">
|
||||
<el-input v-model="sysUserLogTableData.searchParam.ip" :placeholder="t('ipPlaceholder')" />
|
||||
@ -57,7 +60,10 @@ import { t } from '@/lang'
|
||||
import { getLogList } from '@/app/api/site'
|
||||
import UserLogDetail from '@/app/views/auth/components/user-log-detail.vue'
|
||||
import { FormInstance } from 'element-plus'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const sysUserLogTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
|
||||
@ -3,9 +3,15 @@
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
<el-button type="primary" class="w-[100px]" @click="addEvent">
|
||||
{{ t('addMenu') }}
|
||||
</el-button>
|
||||
<div class="flex items-center">
|
||||
<el-button type="primary" class="w-[100px]" @click="addEvent">
|
||||
{{ t('addMenu') }}
|
||||
</el-button>
|
||||
<el-button class="w-[100px]" @click="refreshMenu">
|
||||
{{ t('initializeMenu') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<el-table :data="menusTableData.data" row-key="menu_key" size="large" v-loading="menusTableData.loading">
|
||||
@ -47,8 +53,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { getMenus, deleteMenu } from '@/app/api/sys'
|
||||
import { reactive, ref,h } from 'vue'
|
||||
import { getMenus, deleteMenu,menuRefresh } from '@/app/api/sys'
|
||||
import { t } from '@/lang'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import EditMenu from '@/app/views/auth/components/edit-menu.vue'
|
||||
@ -74,6 +80,27 @@ const getMenuList = () => {
|
||||
})
|
||||
}
|
||||
getMenuList()
|
||||
// 重置菜单
|
||||
const refreshMenu = () => {
|
||||
ElMessageBox.confirm(h('div', null, [
|
||||
h('p', null, t('initializeMenuTipsOne')),
|
||||
h('p', null, t('initializeMenuTipsTwo')),
|
||||
]), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
// type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
menusTableData.loading = true
|
||||
menuRefresh({}).then(res => {
|
||||
menusTableData.loading = false
|
||||
}).catch(() => {
|
||||
menusTableData.loading = false
|
||||
})
|
||||
}).catch(()=>{})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加菜单
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center mt-[16px]">
|
||||
<el-form :inline="true" :model="roleTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('roleName')" prop="seach">
|
||||
<el-input v-model="roleTableData.searchParam.seach" class="w-[240px]" :placeholder="t('roleNamePlaceholder')" />
|
||||
<el-form-item :label="t('roleName')" prop="search">
|
||||
<el-input v-model="roleTableData.searchParam.search" class="w-[240px]" :placeholder="t('roleNamePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadRoleList()">{{ t('search') }}</el-button>
|
||||
@ -51,7 +54,10 @@ import { t } from '@/lang'
|
||||
import { getRoleList, deleteRole } from '@/app/api/sys'
|
||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||
import EditRole from '@/app/views/auth/components/edit-role.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const roleTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
@ -59,7 +65,7 @@ const roleTableData = reactive({
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
seach: ''
|
||||
search: ''
|
||||
}
|
||||
})
|
||||
|
||||
@ -80,7 +86,7 @@ const loadRoleList = (page: number = 1) => {
|
||||
getRoleList({
|
||||
page: roleTableData.page,
|
||||
limit: roleTableData.limit,
|
||||
role_name: roleTableData.searchParam.seach
|
||||
role_name: roleTableData.searchParam.search
|
||||
}).then(res => {
|
||||
roleTableData.loading = false
|
||||
roleTableData.data = res.data.data
|
||||
|
||||
@ -3,20 +3,23 @@
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
<el-button type="primary" class="w-[100px]" @click="addEvent">
|
||||
{{ t('addMenu') }}
|
||||
</el-button>
|
||||
<div class="flex items-center">
|
||||
<el-button type="primary" class="w-[100px]" @click="addEvent">
|
||||
{{ t('addMenu') }}
|
||||
</el-button>
|
||||
<el-button class="w-[100px]" @click="refreshMenu">
|
||||
{{ t('initializeMenu') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="active">
|
||||
<el-tab-pane :label="t('system')" name="system">
|
||||
<el-table :data="menusTableData.system" row-key="menu_key" size="large"
|
||||
v-loading="menusTableData.loading">
|
||||
<el-table :data="menusTableData.system" row-key="menu_key" size="large" v-loading="menusTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !menusTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="menu_name" :show-overflow-tooltip="true" :label="t('menuName')"
|
||||
min-width="150" />
|
||||
<el-table-column prop="menu_name" :show-overflow-tooltip="true" :label="t('menuName')" min-width="150" />
|
||||
<el-table-column :label="t('icon')" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<icon v-if="row.icon" :name="row.icon" size="18px" />
|
||||
@ -33,28 +36,24 @@
|
||||
<el-table-column :label="t('status')" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag class="ml-2" type="success" v-if="row.status == 1">{{ t('statusNormal') }}</el-tag>
|
||||
<el-tag class="ml-2" type="error" v-if="row.status == 0">{{ t('statusDeactivate')
|
||||
}}</el-tag>
|
||||
<el-tag class="ml-2" type="error" v-if="row.status == 0">{{ t('statusDeactivate') }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sort" :label="t('sort')" min-width="100" />
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.menu_key)">{{ t('delete')
|
||||
}}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.menu_key)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="t('application')" name="application">
|
||||
<el-table :data="menusTableData.application" row-key="menu_key" size="large"
|
||||
v-loading="menusTableData.loading">
|
||||
<el-table :data="menusTableData.application" row-key="menu_key" size="large" v-loading="menusTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !menusTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="menu_name" :show-overflow-tooltip="true" :label="t('menuName')"
|
||||
min-width="150" />
|
||||
<el-table-column prop="menu_name" :show-overflow-tooltip="true" :label="t('menuName')" min-width="150" />
|
||||
<el-table-column :label="t('icon')" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<icon v-if="row.icon" :name="row.icon" size="18px" />
|
||||
@ -71,16 +70,14 @@
|
||||
<el-table-column :label="t('status')" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag class="ml-2" type="success" v-if="row.status == 1">{{ t('statusNormal') }}</el-tag>
|
||||
<el-tag class="ml-2" type="error" v-if="row.status == 0">{{ t('statusDeactivate')
|
||||
}}</el-tag>
|
||||
<el-tag class="ml-2" type="error" v-if="row.status == 0">{{ t('statusDeactivate') }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sort" :label="t('sort')" min-width="100" />
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.menu_key)">{{ t('delete')
|
||||
}}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.menu_key)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -93,8 +90,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { getMenus, deleteMenu } from '@/app/api/sys'
|
||||
import { reactive, ref,h } from 'vue'
|
||||
import { getMenus, deleteMenu,menuRefresh } from '@/app/api/sys'
|
||||
import { t } from '@/lang'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import EditMenu from '@/app/views/auth/components/edit-menu.vue'
|
||||
@ -129,7 +126,27 @@ const getMenuList = () => {
|
||||
})
|
||||
}
|
||||
getMenuList()
|
||||
|
||||
// 重置菜单
|
||||
const refreshMenu = () => {
|
||||
ElMessageBox.confirm(h('div', null, [
|
||||
h('p', null, t('initializeMenuTipsOne')),
|
||||
h('p', null, t('initializeMenuTipsTwo')),
|
||||
]), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
// type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
menusTableData.loading = true
|
||||
menuRefresh({}).then(res => {
|
||||
menusTableData.loading = false
|
||||
}).catch(() => {
|
||||
menusTableData.loading = false
|
||||
})
|
||||
}).catch(()=>{})
|
||||
|
||||
}
|
||||
/**
|
||||
* 添加菜单
|
||||
*/
|
||||
|
||||
@ -2,10 +2,12 @@
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center mt-[16px]">
|
||||
<el-form :inline="true" :model="userTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('accountNumber')" prop="seach">
|
||||
<el-input v-model="userTableData.searchParam.seach" class="input-width"
|
||||
:placeholder="t('accountNumberPlaceholder')" />
|
||||
<el-input v-model="userTableData.searchParam.seach" class="input-width" :placeholder="t('accountNumberPlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadUserList()">{{ t('search') }}</el-button>
|
||||
@ -56,10 +58,8 @@
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.is_admin != 1">
|
||||
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="lockEvent(row.uid)" v-if="row.status">{{
|
||||
t('lock') }}</el-button>
|
||||
<el-button type="primary" link @click="unlockEvent(row.uid)" v-else>{{ t('unlock')
|
||||
}}</el-button>
|
||||
<el-button type="primary" link @click="lockEvent(row.uid)" v-if="row.status">{{ t('lock') }}</el-button>
|
||||
<el-button type="primary" link @click="unlockEvent(row.uid)" v-else>{{ t('unlock') }}</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-button link disabled>{{ t('adminDisabled') }}</el-button>
|
||||
@ -89,7 +89,10 @@ import EditUser from '@/app/views/auth/components/edit-user.vue'
|
||||
import { img } from '@/utils/common'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const userTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
|
||||
@ -100,15 +100,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 1">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
1</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">1</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
@ -118,9 +115,7 @@
|
||||
<template #description>
|
||||
<span class="text-[#999]">{{ t("weappAttest") }}</span>
|
||||
<div class="mt-[20px] mb-[40px] h-[32px]">
|
||||
<el-button type="primary"
|
||||
@click="linkEvent('https://open.alipay.com/develop/manage')">{{ t("clickAccess")
|
||||
}}</el-button>
|
||||
<el-button type="primary" @click="linkEvent('https://open.alipay.com/develop/manage')">{{ t("clickAccess") }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-step>
|
||||
@ -131,15 +126,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 2">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
2</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">2</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
@ -149,8 +141,7 @@
|
||||
<template #description>
|
||||
<span class="text-[#999]">{{ t("emplace") }}</span>
|
||||
<div class="mt-[20px] mb-[40px] h-[32px]">
|
||||
<el-button type="primary" plain
|
||||
@click="router.push('/channel/aliapp/config')">{{ t("weappSettingBtn") }}</el-button>
|
||||
<el-button type="primary" plain @click="router.push('/channel/aliapp/config')">{{ t("weappSettingBtn") }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-step>
|
||||
@ -161,15 +152,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 3">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
3</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">3</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
@ -191,15 +179,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 4">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
4</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">4</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
|
||||
@ -13,8 +13,7 @@
|
||||
<h3 class="panel-title !text-sm">{{ t('aliappSet') }}</h3>
|
||||
|
||||
<el-form-item :label="t('aliappName')">
|
||||
<el-input v-model="formData.name" :placeholder="t('aliappNamePlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model="formData.name" :placeholder="t('aliappNamePlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('aliappQrcode')">
|
||||
@ -28,13 +27,11 @@
|
||||
<h3 class="panel-title !text-sm">{{ t('aliappDevelopInfo') }}</h3>
|
||||
|
||||
<el-form-item :label="t('aliappOriginal')">
|
||||
<el-input v-model="formData.private_key" :placeholder="t('aliappOriginalPlaceholder')"
|
||||
class="input-width" clearable />
|
||||
<el-input v-model="formData.private_key" :placeholder="t('aliappOriginalPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('aliappAppid')">
|
||||
<el-input v-model="formData.app_id" :placeholder="t('appidPlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model="formData.app_id" :placeholder="t('appidPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('countersignType')">
|
||||
@ -67,8 +64,7 @@
|
||||
<h3 class="panel-title !text-sm">{{ t('theServerSetting') }}</h3>
|
||||
|
||||
<el-form-item label="AESKey">
|
||||
<el-input v-model="formData.aes_key" :placeholder="t('AESKeyPlaceholder')" class="input-width"
|
||||
show-word-limit clearable />
|
||||
<el-input v-model="formData.aes_key" :placeholder="t('AESKeyPlaceholder')" class="input-width" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
|
||||
|
||||
@ -12,13 +12,10 @@
|
||||
<div class="mt-[20px]">
|
||||
<div class="flex">
|
||||
<div class="min-w-[60px]">
|
||||
<span
|
||||
class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
|
||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="flex items-center text-[14px]">{{ t('alipayCourseTipsOne1') }}--<el-button link
|
||||
type="primary" @click="linkEvent">{{ t('alipayCourseTipsOne2') }}</el-button>, {{
|
||||
t('alipayCourseTipsOne3') }}</p>
|
||||
<p class="flex items-center text-[14px]">{{ t('alipayCourseTipsOne1') }}--<el-button link type="primary" @click="linkEvent">{{ t('alipayCourseTipsOne2') }}</el-button>, {{ t('alipayCourseTipsOne3') }}</p>
|
||||
<div class="w-[100%] mt-[10px]">
|
||||
<img class="w-[100%]" src="@/app/assets/images/setting/alipay1.png" />
|
||||
</div>
|
||||
@ -33,8 +30,7 @@
|
||||
</div>
|
||||
<div class="flex mt-[40px]">
|
||||
<div class="min-w-[60px]">
|
||||
<span
|
||||
class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">2</span>
|
||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">2</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="flex items-center text-[14px]">{{ t('alipayCourseTipsTwo2') }}</p>
|
||||
@ -71,8 +67,7 @@
|
||||
</div>
|
||||
<div class="flex mt-[40px]">
|
||||
<div class="min-w-[60px]">
|
||||
<span
|
||||
class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">3</span>
|
||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">3</span>
|
||||
</div>
|
||||
<div>
|
||||
<!-- <span class="text-primary">{{ t('alipayCourseTipsThree2') }}</span> -->
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<div class="flex ml-[18px] justify-between items-center mt-[20px]">
|
||||
<div class="main-container bg-[#fff] rounded-[4px]">
|
||||
<div class="flex ml-[18px] justify-between items-center pt-[20px]">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" class="page-form" v-loading="loading">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="main-container" v-loading="loading">
|
||||
<div class="flex ml-[18px] justify-between items-center mt-[20px]">
|
||||
<div class="main-container bg-[#fff] rounded-[4px]" v-loading="loading">
|
||||
<div class="flex pl-[18px] justify-between items-center pt-[20px]">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" class="page-form">
|
||||
|
||||
@ -20,15 +20,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 1">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
1</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">1</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
@ -38,8 +35,7 @@
|
||||
<template #description>
|
||||
<span class="text-[#999]">{{ t("weappAttest") }}</span>
|
||||
<div class="mt-[20px] mb-[40px] h-[32px]">
|
||||
<el-button type="primary"
|
||||
@click="linkEvent('https://mp.weixin.qq.com/')">{{ t("clickAccess") }}</el-button>
|
||||
<el-button type="primary" @click="linkEvent('https://mp.weixin.qq.com/')">{{ t("clickAccess") }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-step>
|
||||
@ -50,15 +46,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 2">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
2</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">2</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
@ -68,8 +61,7 @@
|
||||
<template #description>
|
||||
<span class="text-[#999]">{{ t("emplace") }}</span>
|
||||
<div class="mt-[20px] mb-[40px] h-[32px]">
|
||||
<el-button type="primary" plain
|
||||
@click="router.push('/channel/weapp/config')">{{ t("weappSettingBtn") }}</el-button>
|
||||
<el-button type="primary" plain @click="router.push('/channel/weapp/config')">{{ t("weappSettingBtn") }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-step>
|
||||
@ -80,15 +72,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 3">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
3</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">3</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
@ -98,8 +87,7 @@
|
||||
<template #description>
|
||||
<span class="text-[#999]">{{ t("releaseCourse") }}</span>
|
||||
<div class="mt-[20px] mb-[40px] h-[32px]">
|
||||
<el-button type="primary" plain
|
||||
@click="router.push('/channel/weapp/code')">{{ t("weappRelease") }}</el-button>
|
||||
<el-button type="primary" plain @click="router.push('/channel/weapp/code')">{{ t("weappRelease") }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-step>
|
||||
@ -110,15 +98,12 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #icon v-else-if="active == 4">
|
||||
<div
|
||||
class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center">
|
||||
<div class="h-[12px] w-[12px] bg-color rounded-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #icon v-else>
|
||||
<div
|
||||
class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">
|
||||
4</div>
|
||||
<div class="w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full">4</div>
|
||||
</template>
|
||||
<template #title>
|
||||
<p class="text-[14px] text-[#303133] font-[700]">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main-container min-h-[300px] p-5">
|
||||
<div class="main-container min-h-[300px] p-5 bg-[#fff] rounded-[4px]">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
@ -85,7 +85,6 @@ import { AnyObject } from '@/types/global'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const pageName = route.meta.title
|
||||
// const activeNames = ref('1')
|
||||
const dialogVisible = ref(false)
|
||||
const weappTableData:{
|
||||
page: number,
|
||||
|
||||
@ -8,19 +8,16 @@
|
||||
<span class="adorn">|</span>
|
||||
<span class="right">{{ pageName }}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="170px" ref="formRef" :rules="formRules" class="page-form"
|
||||
v-loading="loading">
|
||||
<el-form :model="formData" label-width="170px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('weappInfo') }}</h3>
|
||||
|
||||
<el-form-item :label="t('weappName')" prop="weapp_name">
|
||||
<el-input v-model.trim="formData.weapp_name" :placeholder="t('weappNamePlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model.trim="formData.weapp_name" :placeholder="t('weappNamePlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('weappOriginal')" prop="weapp_original">
|
||||
<el-input v-model.trim="formData.weapp_original" :placeholder="t('weappOriginalPlaceholder')"
|
||||
class="input-width" clearable />
|
||||
<el-input v-model.trim="formData.weapp_original" :placeholder="t('weappOriginalPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('weappQrcode')" prop="qr_code">
|
||||
@ -34,14 +31,12 @@
|
||||
<h3 class="panel-title !text-sm">{{ t('weappDevelopInfo') }}</h3>
|
||||
|
||||
<el-form-item :label="t('weappAppid')" prop="app_id">
|
||||
<el-input v-model.trim="formData.app_id" :placeholder="t('appidPlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model.trim="formData.app_id" :placeholder="t('appidPlaceholder')" class="input-width" clearable />
|
||||
<div class="form-tip">{{ t('weappAppidTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('weappAppsecret')" prop="app_secret">
|
||||
<el-input v-model.trim="formData.app_secret" :placeholder="t('appSecretPlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model.trim="formData.app_secret" :placeholder="t('appSecretPlaceholder')" class="input-width" clearable />
|
||||
<div class="form-tip">{{ t('weappAppsecretTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
@ -63,8 +58,7 @@
|
||||
<h3 class="panel-title !text-sm">{{ t('theServerSetting') }}</h3>
|
||||
|
||||
<el-form-item label="URL">
|
||||
<el-input :model-value="formData.serve_url" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="formData.serve_url" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(formData.serve_url)">{{ t('copy') }}</div>
|
||||
</template>
|
||||
@ -72,8 +66,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Token" prop="token">
|
||||
<el-input v-model.trim="formData.token" :placeholder="t('tokenPlaceholder')" class="input-width"
|
||||
maxlength="32" show-word-limit clearable />
|
||||
<el-input v-model.trim="formData.token" :placeholder="t('tokenPlaceholder')" class="input-width" maxlength="32" show-word-limit clearable />
|
||||
<div class="form-tip">{{ t('tokenTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
@ -101,32 +94,28 @@
|
||||
</div>
|
||||
|
||||
<el-form-item :label="t('requestUrl')">
|
||||
<el-input :model-value="formData.request_url" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="formData.request_url" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(formData.request_url)">{{ t('copy') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('socketUrl')">
|
||||
<el-input :model-value="formData.socket_url" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="formData.socket_url" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(formData.socket_url)">{{ t('copy') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('uploadUrl')">
|
||||
<el-input :model-value="formData.upload_url" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="formData.upload_url" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(formData.upload_url)">{{ t('copy') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('downloadUrl')">
|
||||
<el-input :model-value="formData.download_url" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="formData.download_url" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(formData.download_url)">{{ t('copy') }}</div>
|
||||
</template>
|
||||
|
||||
@ -12,12 +12,10 @@
|
||||
<div class="mt-[20px]">
|
||||
<div class="flex">
|
||||
<div class="min-w-[60px]">
|
||||
<span
|
||||
class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
|
||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="flex items-center text-[14px]">{{ t('writingTipsOne1') }}<el-button link type="primary"
|
||||
@click="linkEvent">{{ t("writingTipsOne2") }}</el-button>,{{ t('writingTipsOne3') }}</p>
|
||||
<p class="flex items-center text-[14px]">{{ t('writingTipsOne1') }}<el-button link type="primary" @click="linkEvent">{{ t("writingTipsOne2") }}</el-button>,{{ t('writingTipsOne3') }}</p>
|
||||
<div class="w-[100%] mt-[10px]">
|
||||
<img class="w-[100%]" src="@/app/assets/images/setting/weapp_1.png" />
|
||||
</div>
|
||||
@ -25,8 +23,7 @@
|
||||
</div>
|
||||
<div class="flex mt-[40px]">
|
||||
<div class="min-w-[60px]">
|
||||
<span
|
||||
class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">2</span>
|
||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">2</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="flex items-center text-[14px]">{{ t('writingTipsTwo1') }}</p>
|
||||
@ -37,12 +34,10 @@
|
||||
</div>
|
||||
<div class="flex mt-[40px]">
|
||||
<div class="min-w-[60px]">
|
||||
<span
|
||||
class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">3</span>
|
||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">3</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="flex items-center text-[14px]">{{ t('writingTipsThree1') }}<span class="text-primary">{{
|
||||
t('writingTipsThree2') }}</span></p>
|
||||
<p class="flex items-center text-[14px]">{{ t('writingTipsThree1') }}<span class="text-primary">{{ t('writingTipsThree2') }}</span></p>
|
||||
<div class="w-[100%] mt-[10px]">
|
||||
<img class="w-[100%]" src="@/app/assets/images/setting/weapp_3.png" />
|
||||
</div>
|
||||
@ -50,8 +45,7 @@
|
||||
</div>
|
||||
<div class="flex mt-[40px]">
|
||||
<div class="min-w-[60px]">
|
||||
<span
|
||||
class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">4</span>
|
||||
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">4</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="flex items-center text-[14px]">{{ t('writingTipsFour1') }}<span class="text-primary">URL /
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main-container p-5">
|
||||
<div class="main-container p-5 bg-[#fff] rounded-[4px]">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
@ -49,8 +49,7 @@
|
||||
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="200">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="infoSwitch(row)">{{ row.is_weapp == 1 ? t('close') :
|
||||
t('open') }}</el-button>
|
||||
<el-button type="primary" link @click="infoSwitch(row)">{{ row.is_weapp == 1 ? t('close') : t('open') }}</el-button>
|
||||
<el-button type="primary" link @click="batchAcquisitionFn(row)">{{ t('regain') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
<el-tab-pane :label="t('wechatAccessFlow')" name="/channel/wechat" />
|
||||
<el-tab-pane :label="t('customMenu')" name="/channel/wechat/menu" />
|
||||
<el-tab-pane :label="t('wechatTemplate')" name="/channel/wechat/message" />
|
||||
<el-tab-pane :label="t('reply')" name="/channel/wechat/reply" />
|
||||
</el-tabs>
|
||||
<div class="p-[20px]">
|
||||
<p class="text-[16px] mb-[20px]">{{ t("wechatInlet") }}</p>
|
||||
|
||||
63
admin/src/app/views/channel/wechat/components/news-card.vue
Normal file
63
admin/src/app/views/channel/wechat/components/news-card.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="attachment-item text-sm mr-[10px] mb-[10px] w-[280px] rounded-lg overflow-hidden border border-color" v-if="data">
|
||||
<div class="relative" @mouseover="hover = true" @mouseout="hover = false">
|
||||
<div class="w-full h-[130px] relative">
|
||||
<el-image :src="data.value.news_item[0].thumb_url" class="w-full h-full"/>
|
||||
<div class="absolute left-0 bottom-0 p-[10px] w-full truncate text-white leading-none" v-if="data.value.news_item.length > 1">
|
||||
{{ data.value.news_item[0].title }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="data.value.news_item.length > 1">
|
||||
<template v-for="(newsItem, newsIndex) in data.value.news_item">
|
||||
<div class="px-[15px] py-[10px] flex" :class="{'border-b border-color' : newsIndex < data.value.news_item.length - 1 }" v-if="newsIndex > 0">
|
||||
<div class="flex-1 w-0 truncate">
|
||||
{{ newsItem.title }}
|
||||
</div>
|
||||
<div class="w-[50px] h-[50px] ml-[10px]">
|
||||
<el-image :src="newsItem.thumb_url" class="w-full h-full"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="px-[15px] py-[10px]" v-else>
|
||||
{{ data.value.news_item[0].title }}
|
||||
</div>
|
||||
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60 cursor-pointer" @click="data = null" v-show="hover && props.mode == 'select'">
|
||||
<icon name="element-Delete" color="#fff" size="40px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'select'
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const hover = ref(false)
|
||||
const data = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
const meta = document.createElement('meta')
|
||||
meta.content = 'same-origin'
|
||||
meta.name = 'referrer'
|
||||
document.getElementsByTagName('head')[0].appendChild(meta)
|
||||
</script>
|
||||
294
admin/src/app/views/channel/wechat/components/reply-form.vue
Normal file
294
admin/src/app/views/channel/wechat/components/reply-form.vue
Normal file
@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<div class="border border-br-light rounded">
|
||||
<div class="py-[10px] px-[30px] flex text-sm border-0 border-b border-br-light text-tx-regular">
|
||||
<div class="pr-[25px] cursor-pointer flex items-center" :class="{'text-primary': formData.msgtype == 'text'}"
|
||||
@click="switchMsgType('text')">
|
||||
<icon name="iconfont-iconxingzhuang-wenzi" size="18" class="mr-[5px]"/>
|
||||
文本
|
||||
</div>
|
||||
<div class="pr-[25px] cursor-pointer flex items-center" :class="{'text-primary': formData.msgtype == 'image'}"
|
||||
@click="switchMsgType('image')">
|
||||
<icon name="iconfont-icontupian" size="18px" class="mr-[5px]"/>
|
||||
图片
|
||||
</div>
|
||||
<div class="pr-[25px] cursor-pointer flex items-center" :class="{'text-primary': formData.msgtype == 'video'}"
|
||||
@click="switchMsgType('video')">
|
||||
<icon name="iconfont-iconshipin1" size="18" class="mr-[5px]"/>
|
||||
视频
|
||||
</div>
|
||||
<div class="pr-[25px] cursor-pointer flex items-center" :class="{'text-primary': formData.msgtype == 'mpnewsarticle'}"
|
||||
@click="switchMsgType('mpnewsarticle')">
|
||||
<icon name="iconfont-icontuwendaohang2" size="13px" class="mr-[5px]"/>
|
||||
图文
|
||||
</div>
|
||||
<div class="pr-[25px] cursor-pointer flex items-center" :class="{'text-primary': formData.msgtype == 'miniprogrampage'}"
|
||||
@click="switchMsgType('miniprogrampage')">
|
||||
<icon name="iconfont-iconxiaochengxu" size="14px" class="mr-[5px]"/>
|
||||
小程序卡片
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-[20px] px-[30px] h-[350px]">
|
||||
<div v-if="formData.msgtype == 'text'">
|
||||
<el-input
|
||||
v-model="formData.text.content" :rows="5" type="textarea" placeholder="" maxlength="600" :show-word-limit="true"
|
||||
resize="none"
|
||||
input-style="box-shadow: none;height:300px"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="formData.msgtype == 'image'" class="flex w-full h-full justify-center items-center image-media">
|
||||
<div class="w-full h-full" v-if="formData.image.url">
|
||||
<upload-image :limit="1" width="150px" height="150px" v-model="formData.image.url"/>
|
||||
</div>
|
||||
<div v-else class="flex w-full h-full justify-center items-center image-media">
|
||||
<div class="flex flex-1 h-full border border-br-light cursor-pointer select-media">
|
||||
<select-wechat-media type="image" @success="setImageMedia">
|
||||
<div class="flex items-center justify-center flex-col">
|
||||
<icon name="element-Plus" size="20px" color="var(--el-text-color-secondary)" />
|
||||
<div class="leading-none text-xs mt-[10px] text-secondary">从素材库选择</div>
|
||||
</div>
|
||||
</select-wechat-media>
|
||||
</div>
|
||||
<div class="flex flex-1 h-full ml-[20px] border border-br-light cursor-pointer">
|
||||
<upload-media type="image" class="w-full h-full flex items-center justify-center" @success="setImageMedia">
|
||||
<div class="flex items-center justify-center flex-col">
|
||||
<icon name="element-Plus" size="20px" color="var(--el-text-color-secondary)" />
|
||||
<div class="leading-none text-xs mt-[10px] text-secondary">上传图片</div>
|
||||
</div>
|
||||
</upload-media>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="formData.msgtype == 'video'" class="flex w-full h-full justify-center items-center video-media">
|
||||
<div class="w-full h-full" v-if="formData.video.url">
|
||||
<upload-video :limit="1" width="150px" height="150px" v-model="formData.video.url"/>
|
||||
</div>
|
||||
<div v-else class="flex w-full h-full justify-center items-center video-media">
|
||||
<div class="flex flex-1 h-full border border-br-light cursor-pointer select-media">
|
||||
<select-wechat-media type="video" @success="setVideoMedia">
|
||||
<div class="flex items-center justify-center flex-col">
|
||||
<icon name="element-Plus" size="20px" color="var(--el-text-color-secondary)" />
|
||||
<div class="leading-none text-xs mt-[10px] text-secondary">从素材库选择</div>
|
||||
</div>
|
||||
</select-wechat-media>
|
||||
</div>
|
||||
<div class="flex flex-1 h-full ml-[20px] border border-br-light cursor-pointer">
|
||||
<upload-media type="video" class="w-full h-full flex items-center justify-center" @success="setVideoMedia">
|
||||
<div class="flex items-center justify-center flex-col">
|
||||
<icon name="element-Plus" size="20px" color="var(--el-text-color-secondary)" />
|
||||
<div class="leading-none text-xs mt-[10px] text-secondary">上传视频</div>
|
||||
</div>
|
||||
</upload-media>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="formData.msgtype == 'mpnewsarticle'" class="flex w-full h-full justify-center items-center image-media">
|
||||
<div class="w-full h-full" v-if="formData.mpnewsarticle">
|
||||
<news-card v-model="formData.mpnewsarticle"/>
|
||||
</div>
|
||||
<div v-else class="flex w-full h-full justify-center items-center image-media">
|
||||
<div class="flex flex-1 h-full border border-br-light cursor-pointer select-media">
|
||||
<select-wechat-media type="news" @success="setNewsMedia">
|
||||
<div class="flex items-center justify-center flex-col">
|
||||
<icon name="element-Plus" size="20px" color="var(--el-text-color-secondary)" />
|
||||
<div class="leading-none text-xs mt-[10px] text-secondary">从素材库选择</div>
|
||||
</div>
|
||||
</select-wechat-media>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="formData.msgtype == 'miniprogrampage'">
|
||||
<el-form :model="formData.miniprogrampage" label-width="140px" class="page-form" ref="formRef" :rules="formRules">
|
||||
<el-form-item label="小程序APPID" prop="appid">
|
||||
<el-input v-model="formData.miniprogrampage.appid" class="input-width"/>
|
||||
<div class="form-tip">小程序需已经与公众号关联</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="小程序卡片标题" prop="title">
|
||||
<el-input v-model="formData.miniprogrampage.title" class="input-width"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="小程序的页面路径" prop="pagepath">
|
||||
<el-input v-model="formData.miniprogrampage.pagepath" class="input-width"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="小程序卡片图片" prop="thumb_media_url">
|
||||
<upload-image :limit="1" width="100px" height="100px" v-model="formData.miniprogrampage.thumb_media_url" v-if="formData.miniprogrampage.thumb_media_url"/>
|
||||
<select-wechat-media type="image" @success="setWeappImageMedia" v-else>
|
||||
<div class="rounded cursor-pointer overflow-hidden relative border border-solid border-color image-wrap mr-[10px] w-[100px] h-[100px]">
|
||||
<div class="w-full h-full flex items-center justify-center flex-col content-wrap">
|
||||
<icon name="element-Plus" size="20px" color="var(--el-text-color-secondary)" />
|
||||
<div class="leading-none text-xs mt-[10px] text-secondary">{{ t('upload.root') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</select-wechat-media>
|
||||
<div class="form-tip">小程序卡片图片建议大小为520*416</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import UploadMedia from '@/app/views/channel/wechat/components/upload-media.vue'
|
||||
import SelectWechatMedia from '@/app/views/channel/wechat/components/select-wechat-media.vue'
|
||||
import Test from '@/utils/test'
|
||||
import { ElMessage, FormInstance, FormRules } from 'element-plus'
|
||||
import NewsCard from '@/app/views/channel/wechat/components/news-card.vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const formData = ref({
|
||||
msgtype: 'text',
|
||||
text: {
|
||||
content: ''
|
||||
},
|
||||
image: {
|
||||
media_id: '',
|
||||
url: ''
|
||||
},
|
||||
video: {
|
||||
media_id: '',
|
||||
url: ''
|
||||
},
|
||||
miniprogrampage: {
|
||||
appid: '',
|
||||
title: '',
|
||||
pagepath: '',
|
||||
thumb_media_url: '',
|
||||
thumb_media_id: ''
|
||||
},
|
||||
mpnewsarticle: null
|
||||
})
|
||||
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const switchMsgType = (type: string) => {
|
||||
formData.value.msgtype = type
|
||||
}
|
||||
|
||||
const setImageMedia = (data: any) => {
|
||||
formData.value.image.media_id = data.media_id
|
||||
formData.value.image.url = data.value
|
||||
}
|
||||
|
||||
const setVideoMedia = (data: any) => {
|
||||
formData.value.video.media_id = data.media_id
|
||||
formData.value.video.url = data.value
|
||||
}
|
||||
|
||||
const setWeappImageMedia = (data: any) => {
|
||||
formData.value.miniprogrampage.thumb_media_id = data.media_id
|
||||
formData.value.miniprogrampage.thumb_media_url = data.value
|
||||
}
|
||||
|
||||
const setNewsMedia = (data: any) => {
|
||||
formData.value.mpnewsarticle = {
|
||||
article_id: data.media_id,
|
||||
value: data.value
|
||||
}
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
appid: [
|
||||
{ required: true, message: '请填写小程序appid', trigger: 'blur' }
|
||||
],
|
||||
title: [
|
||||
{ required: true, message: '请填写小程序卡片标题', trigger: 'blur' }
|
||||
],
|
||||
pagepath: [
|
||||
{ required: true, message: '请填写小程序卡片跳转页面', trigger: 'blur' }
|
||||
],
|
||||
thumb_media_url: [
|
||||
{ required: true, message: '请上传小程序卡片封面', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
*/
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
switch (formData.value.msgtype) {
|
||||
case 'text':
|
||||
if (Test.empty(formData.value.text.content)) {
|
||||
ElMessage({ message: '请输入回复内容', type: 'warning' })
|
||||
verify = false
|
||||
}
|
||||
break
|
||||
case 'image':
|
||||
if (Test.empty(formData.value.image.url)) {
|
||||
ElMessage({ message: '请上传回复图片', type: 'warning' })
|
||||
verify = false
|
||||
}
|
||||
break
|
||||
case 'video':
|
||||
if (Test.empty(formData.value.video.url)) {
|
||||
ElMessage({ message: '请上传回复视频', type: 'warning' })
|
||||
verify = false
|
||||
}
|
||||
break
|
||||
case 'miniprogrampage':
|
||||
await formRef.value.validate(async (valid) => {
|
||||
verify = valid
|
||||
})
|
||||
break
|
||||
case 'mpnewsarticle':
|
||||
if (Test.empty(formData.value.mpnewsarticle)) {
|
||||
ElMessage({ message: '请选择图文', type: 'warning' })
|
||||
verify = false
|
||||
}
|
||||
break
|
||||
}
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.image-media, .video-media) {
|
||||
.el-upload {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
:deep(.select-media) {
|
||||
& > div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div @click="openDialog">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<el-dialog v-model="showDialog" :title="t('upload.select' + type)" width="60%" class="attachment-dialog"
|
||||
:destroy-on-close="true">
|
||||
<div class="flex border-t border-b main-wrap border-color w-full h-[40vh]">
|
||||
<!-- 素材 -->
|
||||
<div class="attachment-list-wrap flex flex-col p-[15px] flex-1 overflow-hidden">
|
||||
<el-row :gutter="15" class="h-[32px]">
|
||||
<el-col :span="10">
|
||||
<div class="flex" v-if="prop.type != 'news'">
|
||||
<upload-media :type="prop.type" @success="getAttachmentList()">
|
||||
<el-button type="primary">{{ t('upload.upload' + type) }}</el-button>
|
||||
</upload-media>
|
||||
</div>
|
||||
<div class="flex" v-else>
|
||||
<el-button type="primary" :loading="syncLoading" @click="syncWechatNews">{{ syncLoading ? '同步中' : '同步微信图文'}}</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="flex-1 my-[15px] h-0" v-loading="attachment.loading">
|
||||
<el-scrollbar>
|
||||
<!-- 素材管理 -->
|
||||
<div v-if="attachment.data.length">
|
||||
<div class="flex flex-wrap" v-if="prop.type != 'news'">
|
||||
<div class="attachment-item mr-[10px] w-[120px]" v-for="(item, index) in attachment.data"
|
||||
:key="index" @click="selectedFile = item">
|
||||
<div
|
||||
class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
|
||||
<el-image :src="img(item.value)" fit="contain" v-if="type == 'image'"
|
||||
:preview-src-list="item.image_list" />
|
||||
<video :src="img(item.value)" v-else-if="type == 'video'"></video>
|
||||
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
||||
<icon name="element-Select" color="#fff" size="40px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative" ref="waterfallContainerRef" v-else>
|
||||
<div ref="waterfallItemRef" class="absolute attachment-item mr-[10px] mb-[10px] w-[280px] rounded-lg overflow-hidden border border-color" v-for="(item, index) in attachment.data"
|
||||
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }"
|
||||
:key="index" @click="selectedFile = item">
|
||||
<div class="relative">
|
||||
<div class="w-full h-[130px] relative">
|
||||
<el-image :src="item.value.news_item[0].thumb_url" class="w-full h-full"/>
|
||||
<div class="absolute left-0 bottom-0 p-[10px] w-full truncate text-white leading-none" v-if="item.value.news_item.length > 1">
|
||||
{{ item.value.news_item[0].title }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.value.news_item.length > 1">
|
||||
<template v-for="(newsItem, newsIndex) in item.value.news_item">
|
||||
<div class="px-[15px] py-[10px] flex" :class="{'border-b border-color' : newsIndex < item.value.news_item.length - 1 }" v-if="newsIndex > 0">
|
||||
<div class="flex-1 w-0 truncate">
|
||||
{{ newsItem.title }}
|
||||
</div>
|
||||
<div class="w-[50px] h-[50px] ml-[10px]">
|
||||
<el-image :src="newsItem.thumb_url" class="w-full h-full"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="px-[15px] py-[10px]" v-else>
|
||||
{{ item.value.news_item[0].title }}
|
||||
</div>
|
||||
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
||||
<icon name="element-Select" color="#fff" size="40px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center" v-else>
|
||||
<el-empty v-if="!attachment.loading"
|
||||
:description="t('upload.mediaEmpty')"
|
||||
:image-size="100" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col span="24">
|
||||
<div class="flex h-full justify-end items-center">
|
||||
<el-pagination v-model:current-page="attachment.page" :small="true"
|
||||
v-model:page-size="attachment.limit" :page-sizes="[10, 20, 30, 40, 60]"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="attachment.total"
|
||||
@size-change="getAttachmentList()" @current-change="getAttachmentList" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, nextTick } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import UploadMedia from './upload-media.vue'
|
||||
import { img, debounce } from '@/utils/common'
|
||||
import { getMediaList, syncNews } from '@/app/api/wechat'
|
||||
|
||||
const prop = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
}
|
||||
})
|
||||
|
||||
const showDialog = ref(false)
|
||||
|
||||
const openDialog = () => {
|
||||
prop.type == 'news' && waterfall()
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
const attachment: Record<string, any> = reactive({
|
||||
loading: true,
|
||||
page: 1,
|
||||
total: 0,
|
||||
limit: 10,
|
||||
data: []
|
||||
})
|
||||
|
||||
/**
|
||||
* 查询素材
|
||||
*/
|
||||
const getAttachmentList = (page: number = 1) => {
|
||||
attachment.loading = true
|
||||
attachment.page = page
|
||||
getMediaList({
|
||||
page: attachment.page,
|
||||
limit: attachment.limit,
|
||||
type: prop.type
|
||||
}).then(res => {
|
||||
attachment.data = res.data.data
|
||||
attachment.total = res.data.total
|
||||
attachment.loading = false
|
||||
prop.type == 'news' && waterfall()
|
||||
}).catch(() => {
|
||||
attachment.loading = false
|
||||
})
|
||||
}
|
||||
getAttachmentList()
|
||||
|
||||
const emits = defineEmits(['success'])
|
||||
const selectedFile: Record<string, any> = ref({})
|
||||
|
||||
const confirm = () => {
|
||||
emits('success', selectedFile.value)
|
||||
}
|
||||
|
||||
const syncLoading = ref(false)
|
||||
const syncWechatNews = () => {
|
||||
if (syncLoading.value) return
|
||||
syncLoading.value = true
|
||||
|
||||
syncNews().then(() => {
|
||||
syncLoading.value = false
|
||||
getAttachmentList()
|
||||
}).catch(() => {
|
||||
syncLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const meta = document.createElement('meta')
|
||||
meta.content = 'same-origin'
|
||||
meta.name = 'referrer'
|
||||
document.getElementsByTagName('head')[0].appendChild(meta)
|
||||
|
||||
// 瀑布流计算
|
||||
const waterfallContainerRef = ref(null)
|
||||
const waterfallItemRef = ref([])
|
||||
const listPosition = ref([])
|
||||
const waterfall = debounce(() => {
|
||||
nextTick(() => {
|
||||
const containerWidth = waterfallContainerRef.value.clientWidth
|
||||
const column = parseInt(containerWidth / 292)
|
||||
const heights = []
|
||||
const positions = []
|
||||
|
||||
waterfallItemRef.value.forEach((item, i) => {
|
||||
if (i < column) {
|
||||
const position = {}
|
||||
position.top = '0px'
|
||||
if (i % column == 0) {
|
||||
position.left = item.clientWidth * i + "px"
|
||||
} else {
|
||||
position.left = item.clientWidth * i + (i % column * 10) + "px"
|
||||
}
|
||||
positions[i] = position
|
||||
heights[i] = item.clientHeight + 10
|
||||
} else {
|
||||
let minHeight = Math.min(...heights) // 找到第一列的最小高度
|
||||
let minIndex = heights.findIndex(item => item ===
|
||||
minHeight) // 找到最小高度的索引
|
||||
let position = {}
|
||||
position.top = minHeight + 10 + "px"
|
||||
position.left = positions[minIndex].left
|
||||
positions[i] = position
|
||||
heights[minIndex] += item.clientHeight + 10
|
||||
}
|
||||
})
|
||||
listPosition.value = positions
|
||||
})
|
||||
}, 800)
|
||||
|
||||
// 重新布局,以适应窗口变化
|
||||
window.addEventListener('resize', () => waterfall())
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<el-upload v-bind="upload" ref="uploadRef">
|
||||
<slot></slot>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { getToken } from '@/utils/common'
|
||||
import storage from '@/utils/storage'
|
||||
import { ElMessage, UploadFile, UploadFiles } from 'element-plus'
|
||||
|
||||
const prop = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['success'])
|
||||
|
||||
const uploadRef = ref<Record<string, any> | null>(null)
|
||||
// 上传文件
|
||||
const upload = computed(() => {
|
||||
const headers: Record<string, any> = {}
|
||||
headers[import.meta.env.VITE_REQUEST_HEADER_TOKEN_KEY] = getToken()
|
||||
headers[import.meta.env.VITE_REQUEST_HEADER_SITEID_KEY] = storage.get('siteId') || 0
|
||||
|
||||
return {
|
||||
action: `${import.meta.env.VITE_APP_BASE_URL}/wechat/media/${prop.type}`,
|
||||
multiple: true,
|
||||
headers,
|
||||
accept: prop.type == 'image' ? '.bmp,.png,.jpeg,.jpg,.gif' : '.mp4',
|
||||
onSuccess: (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
|
||||
if (response.code >= 1) {
|
||||
emits('success', response.data)
|
||||
uploadRef.value?.handleRemove(uploadFile)
|
||||
} else {
|
||||
uploadFile.status = 'fail'
|
||||
uploadRef.value?.handleRemove(uploadFile)
|
||||
ElMessage({ message: response.msg, type: 'error' })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -9,19 +9,16 @@
|
||||
<span class="right">{{ pageName }}</span>
|
||||
</div>
|
||||
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form"
|
||||
v-loading="loading">
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('wechatInfo') }}</h3>
|
||||
|
||||
<el-form-item :label="t('wechatName')" prop="wechat_name">
|
||||
<el-input v-model.trim="formData.wechat_name" :placeholder="t('wechatNamePlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model.trim="formData.wechat_name" :placeholder="t('wechatNamePlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('wechatOriginal')" prop="wechat_original">
|
||||
<el-input v-model.trim="formData.wechat_original" :placeholder="t('wechatOriginalPlaceholder')"
|
||||
class="input-width" clearable />
|
||||
<el-input v-model.trim="formData.wechat_original" :placeholder="t('wechatOriginalPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('wechatQrcode')" prop="qr_code">
|
||||
@ -35,14 +32,12 @@
|
||||
<h3 class="panel-title !text-sm">{{ t('wechatDevelopInfo') }}</h3>
|
||||
|
||||
<el-form-item :label="t('wechatAppid')" prop="app_id">
|
||||
<el-input v-model.trim="formData.app_id" :placeholder="t('appidPlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model.trim="formData.app_id" :placeholder="t('appidPlaceholder')" class="input-width" clearable />
|
||||
<div class="form-tip">{{ t('wechatAppidTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('wechatAppsecret')" prop="app_secret">
|
||||
<el-input v-model.trim="formData.app_secret" :placeholder="t('appSecretPlaceholder')" class="input-width"
|
||||
clearable />
|
||||
<el-input v-model.trim="formData.app_secret" :placeholder="t('appSecretPlaceholder')" class="input-width" clearable />
|
||||
<div class="form-tip">{{ t('wechatAppsecretTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
@ -52,8 +47,7 @@
|
||||
<h3 class="panel-title !text-sm">{{ t('theServerSetting') }}</h3>
|
||||
|
||||
<el-form-item label="URL">
|
||||
<el-input :model-value="wechatStatic.serve_url" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="wechatStatic.serve_url" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(wechatStatic.serve_url)">{{ t('copy') }}</div>
|
||||
</template>
|
||||
@ -61,14 +55,12 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Token" prop="token">
|
||||
<el-input v-model.trim="formData.token" :placeholder="t('tokenPlaceholder')" class="input-width"
|
||||
maxlength="32" show-word-limit clearable />
|
||||
<el-input v-model.trim="formData.token" :placeholder="t('tokenPlaceholder')" class="input-width" maxlength="32" show-word-limit clearable />
|
||||
<div class="form-tip">{{ t('tokenTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="EncodingAESKey" prop="encoding_aes_key">
|
||||
<el-input v-model.trim="formData.encoding_aes_key" :placeholder="t('encodingAesKeyPlaceholder')"
|
||||
class="input-width" maxlength="43" show-word-limit clearable />
|
||||
<el-input v-model.trim="formData.encoding_aes_key" :placeholder="t('encodingAesKeyPlaceholder')" class="input-width" maxlength="43" show-word-limit clearable />
|
||||
<div class="form-tip">{{ t('encodingAESKeyTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
@ -94,8 +86,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('businessDomain')">
|
||||
<el-input :model-value="wechatStatic.business_domain" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="wechatStatic.business_domain" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
|
||||
</div>
|
||||
@ -104,8 +95,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('jsSecureDomain')">
|
||||
<el-input :model-value="wechatStatic.js_secure_domain" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="wechatStatic.js_secure_domain" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
|
||||
</div>
|
||||
@ -114,8 +104,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('webAuthDomain')">
|
||||
<el-input :model-value="wechatStatic.web_auth_domain" placeholder="Please input" class="input-width"
|
||||
:readonly="true">
|
||||
<el-input :model-value="wechatStatic.web_auth_domain" placeholder="Please input" class="input-width" :readonly="true">
|
||||
<template #append>
|
||||
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
|
||||
</div>
|
||||
|
||||
178
admin/src/app/views/channel/wechat/keyword_reply_edit.vue
Normal file
178
admin/src/app/views/channel/wechat/keyword_reply_edit.vue
Normal file
@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<div class="detail-head">
|
||||
<div class="left" @click="router.push({ path: '/channel/wechat/reply' })">
|
||||
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
|
||||
<span class="ml-[1px]">{{ t('returnToPreviousPage') }}</span>
|
||||
</div>
|
||||
<span class="adorn">|</span>
|
||||
<span class="right">{{ pageName }}</span>
|
||||
</div>
|
||||
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
|
||||
<el-form-item :label="t('ruleName')" prop="name">
|
||||
<el-input v-model.trim="formData.name" :placeholder="t('ruleNamePlaceholder')" class="input-width" clearable maxlength="60"/>
|
||||
<div class="form-tip">{{ t('ruleNameTips') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('keyword')" prop="keyword">
|
||||
<el-input v-model.trim="formData.keyword" :placeholder="t('keywordPlaceholder')" class="input-width" clearable >
|
||||
<template #prepend>
|
||||
<el-select v-model="formData.matching_type" placeholder="Select" style="width: 115px">
|
||||
<el-option :label="t('allMatching')" value="full" />
|
||||
<el-option :label="t('fuzzyMatching')" value="like" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('content')" prop="content">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center" v-for="(item, index) in formData.content">
|
||||
<div class="w-[300px] bg-page p-[10px] mr-[10px] mb-[10px] rounded leading-none" v-if="item.msgtype == 'text'">
|
||||
{{ item.text.content }}
|
||||
</div>
|
||||
<div class="w-[300px] bg-page p-[10px] mr-[10px] mb-[10px] rounded" v-if="item.msgtype == 'image'">
|
||||
<upload-image :limit="1" width="120px" height="120px" v-model="item.image.url"/>
|
||||
</div>
|
||||
<div class="w-[300px] bg-page p-[10px] mr-[10px] mb-[10px] rounded" v-if="item.msgtype == 'video'">
|
||||
<upload-video :limit="1" width="120px" height="120px" v-model="item.video.url"/>
|
||||
</div>
|
||||
<div class="w-[300px] bg-page p-[10px] mr-[10px] mb-[10px] rounded" v-if="item.msgtype == 'mpnewsarticle'">
|
||||
<news-card v-model="item.mpnewsarticle" mode="show"/>
|
||||
</div>
|
||||
<div class="w-[300px] bg-page p-[10px] mr-[10px] mb-[10px] rounded" v-if="item.msgtype == 'miniprogrampage'">
|
||||
小程序卡片【{{ item.miniprogrampage.appid }}】
|
||||
</div>
|
||||
<icon name="element-Delete" class="cursor-pointer" @click="removeContent(index)"/>
|
||||
</div>
|
||||
<div class="mt-[10px]">
|
||||
<el-button type="primary" @click="showDialog = true">{{ t('addReplyContent') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('replyMethod')" prop="reply_method">
|
||||
<el-radio-group v-model="formData.reply_method">
|
||||
<el-radio label="all">{{ t('replyMethodAll') }}</el-radio>
|
||||
<el-radio label="rand">{{ t('replyMethodRand') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-form>
|
||||
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="showDialog" :title="t('addReplyContent')" width="60%"
|
||||
:destroy-on-close="true">
|
||||
<reply-form v-model="replyContent" ref="ReplyRef"/>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="addReplyContent">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getKeywordsReplyInfo, editKeywordsReply, addKeywordsReply } from '@/app/api/wechat'
|
||||
import { ElMessage, FormInstance, FormRules } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import ReplyForm from '@/app/views/channel/wechat/components/reply-form.vue'
|
||||
import NewsCard from '@/app/views/channel/wechat/components/news-card.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const pageName = route.meta.title
|
||||
const showDialog = ref(false)
|
||||
|
||||
const formData = reactive<Record<string, string>>({
|
||||
id: 0,
|
||||
name: '',
|
||||
keyword: '',
|
||||
content: [],
|
||||
matching_type: 'full',
|
||||
reply_method: 'all'
|
||||
})
|
||||
|
||||
const replyContent = ref({})
|
||||
const ReplyRef = ref(null)
|
||||
|
||||
const addReplyContent = () => {
|
||||
ReplyRef.value?.verify().then(res => {
|
||||
if (res) {
|
||||
formData.content.push(replyContent.value)
|
||||
replyContent.value = {}
|
||||
showDialog.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const removeContent = (index: number) => {
|
||||
formData.content.splice(index, 1)
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
name: [
|
||||
{ required: true, message: t('ruleNamePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
keyword: [
|
||||
{ required: true, message: t('keywordPlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
content: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (!formData.content.length) callback(new Error(t('contentPlaceholder')))
|
||||
callback()
|
||||
}, trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
if (route.query.id) {
|
||||
getKeywordsReplyInfo(route.query.id).then(({ data }) => {
|
||||
Object.keys(formData).forEach((key: string) => {
|
||||
if (data[key] != undefined) formData[key] = data[key]
|
||||
})
|
||||
loading.value = false
|
||||
}).catch()
|
||||
} else {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
const save = async (formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const api = formData.id ? editKeywordsReply : addKeywordsReply
|
||||
loading.value = true
|
||||
api(formData).then(() => {
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main-container p-5">
|
||||
<div class="main-container p-5 bg-[#fff] rounded-[4px]">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
@ -7,6 +7,7 @@
|
||||
<el-tab-pane :label="t('wechatAccessFlow')" name="/channel/wechat" />
|
||||
<el-tab-pane :label="t('customMenu')" name="/channel/wechat/menu" />
|
||||
<el-tab-pane :label="t('wechatTemplate')" name="/channel/wechat/message" />
|
||||
<el-tab-pane :label="t('reply')" name="/channel/wechat/reply" />
|
||||
</el-tabs>
|
||||
<div class="flex" v-loading="loading">
|
||||
<div class="preview-wrap w-[300px] h-[550px] mr-[16px] bg-overlay rounded-md flex flex-col justify-between border border-color">
|
||||
|
||||
@ -1,8 +1,183 @@
|
||||
<template>
|
||||
<div></div>
|
||||
<div class="w-full p-5 bg-body">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ t('title') }}</span>
|
||||
</div>
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleClick">
|
||||
<el-tab-pane :label="t('wechatAccessFlow')" name="/channel/wechat" />
|
||||
<el-tab-pane :label="t('customMenu')" name="/channel/wechat/menu" />
|
||||
<el-tab-pane :label="t('wechatTemplate')" name="/channel/wechat/message" />
|
||||
<el-tab-pane :label="t('reply')" name="/channel/wechat/reply" />
|
||||
</el-tabs>
|
||||
<div class="py-[20px]">
|
||||
<el-radio-group v-model="replyType" style="margin-bottom: 30px">
|
||||
<el-radio-button label="keyword">{{ t('keywordReply') }}</el-radio-button>
|
||||
<el-radio-button label="default">{{ t('defaultReply') }}</el-radio-button>
|
||||
<el-radio-button label="subscribe">{{ t('subscribeReply') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
<div v-show="replyType == 'keyword'">
|
||||
<div class="flex justify-between items-center">
|
||||
<el-button type="primary" @click="addKeywordsReply">新建回复</el-button>
|
||||
</div>
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="replyTableData.data" size="large" v-loading="replyTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !replyTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="name" label="规则名称" min-width="120" />
|
||||
<el-table-column prop="keyword" label="关键字" min-width="120" />
|
||||
<el-table-column label="匹配规则" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.matching_type == 'full' ? '全匹配' : '模糊匹配' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回复方式" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.reply_method == 'all' ? '全部回复' : '随机回复一条' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="editKeywordsReply(row)">{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteKeyword(row)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="replyTableData.page" v-model:page-size="replyTableData.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="replyTableData.total"
|
||||
@size-change="loadKeywordsReplyList()" @current-change="loadKeywordsReplyList" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="replyType == 'default'">
|
||||
<reply-form v-model="defaultReply" ref="defaultReplyRef"/>
|
||||
<div class="mt-[20px]">
|
||||
<el-button type="primary" :loading="loading" @click="save()">{{ t('save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="replyType == 'subscribe'">
|
||||
<reply-form v-model="subscribeReply" ref="subscribeReplyRef"/>
|
||||
<div class="mt-[20px]">
|
||||
<el-button type="primary" :loading="loading" @click="save()">{{ t('save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { t } from '@/lang'
|
||||
import {
|
||||
getKeywordsReplyList,
|
||||
getDefaultReply,
|
||||
getSubscribeReply,
|
||||
setDefaultReply,
|
||||
setSubscribeReply,
|
||||
delKeywordsReply
|
||||
} from '@/app/api/wechat'
|
||||
import ReplyForm from '@/app/views/channel/wechat/components/reply-form.vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const activeName = ref('/channel/wechat/reply')
|
||||
const replyType = ref('keyword')
|
||||
|
||||
const addKeywordsReply = () => {
|
||||
router.push('/channel/wechat/keyword_reply_edit')
|
||||
}
|
||||
|
||||
const editKeywordsReply = (row: Object) => {
|
||||
router.push('/channel/wechat/keyword_reply_edit?id=' + row.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*/
|
||||
const deleteKeyword = (row: Object) => {
|
||||
ElMessageBox.confirm(t('replyDeleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
delKeywordsReply(row.id).then(() => {
|
||||
loadKeywordsReplyList()
|
||||
}).catch(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
const handleClick = (val: any) => {
|
||||
router.push({ path: activeName.value })
|
||||
}
|
||||
|
||||
const defaultReply = ref({})
|
||||
const subscribeReply = ref({})
|
||||
|
||||
getDefaultReply().then(({ data }) => {
|
||||
data.length != 0 && (defaultReply.value = data.content)
|
||||
}).catch()
|
||||
|
||||
getSubscribeReply().then(({ data }) => {
|
||||
data.length != 0 && (subscribeReply.value = data.content)
|
||||
}).catch()
|
||||
|
||||
const defaultReplyRef = ref(null)
|
||||
const subscribeReplyRef = ref(null)
|
||||
|
||||
const save = async () => {
|
||||
let verify = true,
|
||||
api,
|
||||
data = {}
|
||||
|
||||
switch (replyType.value) {
|
||||
case 'default':
|
||||
await defaultReplyRef.value?.verify().then(res => {
|
||||
verify = res
|
||||
})
|
||||
api = setDefaultReply
|
||||
data = defaultReply.value
|
||||
break
|
||||
case 'subscribe':
|
||||
await subscribeReplyRef.value?.verify().then(res => {
|
||||
verify = res
|
||||
})
|
||||
api = setSubscribeReply
|
||||
data = subscribeReply.value
|
||||
break
|
||||
}
|
||||
if (verify) {
|
||||
api({content: data}).then(() => {}).catch()
|
||||
}
|
||||
}
|
||||
|
||||
const replyTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: []
|
||||
})
|
||||
|
||||
const loadKeywordsReplyList = (page: number = 1) => {
|
||||
replyTableData.loading = true
|
||||
replyTableData.page = page
|
||||
|
||||
getKeywordsReplyList({
|
||||
page: replyTableData.page,
|
||||
limit: replyTableData.limit
|
||||
}).then(res => {
|
||||
replyTableData.loading = false
|
||||
replyTableData.data = res.data.data
|
||||
replyTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
replyTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadKeywordsReplyList()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main-container p-5">
|
||||
<div class="main-container p-5 bg-[#fff] rounded-[4px]">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
@ -7,6 +7,7 @@
|
||||
<el-tab-pane :label="t('wechatAccessFlow')" name="/channel/wechat" />
|
||||
<el-tab-pane :label="t('customMenu')" name="/channel/wechat/menu" />
|
||||
<el-tab-pane :label="t('wechatTemplate')" name="/channel/wechat/message" />
|
||||
<el-tab-pane :label="t('reply')" name="/channel/wechat/reply" />
|
||||
</el-tabs>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
|
||||
@ -247,7 +247,7 @@ import { ElTable } from 'element-plus'
|
||||
import Sortable from 'sortablejs'
|
||||
import { range } from 'lodash-es'
|
||||
|
||||
import { getDiyPageList } from '@/app/api/diy'
|
||||
import { getDiyPageListByCarouselSearch } from '@/app/api/diy'
|
||||
|
||||
const diyStore = useDiyStore()
|
||||
diyStore.editComponent.ignore = ['componentBgColor','componentBgUrl','marginTop','marginBottom','topRounded','bottomRounded','pageBgColor','marginBoth'] // 忽略公共属性
|
||||
@ -394,7 +394,6 @@ const diyPageTable = reactive({
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
type: 'DIY_PAGE' // todo 数据筛选,要考虑只能查询 微页面类型的数据
|
||||
}
|
||||
})
|
||||
const diyPageTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
@ -406,7 +405,7 @@ const loadDiyPageList = (page: number = 1) => {
|
||||
diyPageTable.loading = true
|
||||
diyPageTable.page = page
|
||||
|
||||
getDiyPageList({
|
||||
getDiyPageListByCarouselSearch({
|
||||
page: diyPageTable.page,
|
||||
limit: diyPageTable.limit,
|
||||
...diyPageTable.searchParam
|
||||
@ -419,7 +418,7 @@ const loadDiyPageList = (page: number = 1) => {
|
||||
// 排除当前编辑的微页面以及存在 置顶组件的数据
|
||||
if (diyStore.id) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].id == diyStore.id || data[i].value.indexOf('top_fixed') != -1) {
|
||||
if (data[i].id == diyStore.id) {
|
||||
isExistCount++;
|
||||
} else {
|
||||
newData.push(data[i]);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-wrap mt-[20px] min-w-[1200px]" v-if="page.use_template">
|
||||
<div class="flex flex-wrap pt-[20px] min-w-[1200px] bg-[#fff] rounded-[4px]" v-if="page.use_template">
|
||||
<div class="page-item relative bg-no-repeat ml-[20px] mr-[40px] bg-[#f7f7f7] w-[340px] pt-[90px] pb-[20px]">
|
||||
<p class="absolute top-[54px] left-[50%] translate-x-[-50%] text-[14px] truncate w-[130px] text-center">{{ page.use_template.title }}</p>
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-wrap mt-[20px] min-w-[1200px]" v-if="page.use_template">
|
||||
<div class="flex flex-wrap pt-[20px] min-w-[1200px] bg-[#fff] rounded-[4px]" v-if="page.use_template">
|
||||
<div class="page-item relative bg-no-repeat ml-[20px] mr-[40px] bg-[#f7f7f7] w-[340px] pt-[90px] pb-[20px]">
|
||||
<p class="absolute top-[54px] left-[50%] translate-x-[-50%] text-[14px] truncate w-[130px] text-center">{{ page.use_template.title }}</p>
|
||||
|
||||
|
||||
@ -165,7 +165,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getAccountList, getAccountStat, getAccountType } from '@/app/api/site'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
@ -191,6 +191,11 @@ const siteAccountLogTable = reactive({
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
// 去空操作
|
||||
watch(() => siteAccountLogTable.searchParam.trade_no, (nval) => {
|
||||
siteAccountLogTable.searchParam.trade_no = nval.trim();
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取站点账单记录列表
|
||||
*/
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between items-center py-[24px] pl-[62px] pr-[64px] home-head">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center" v-if="webConfig">
|
||||
<img class="w-[32x] h-[32px] rounded-full" v-if="webConfig.icon" :src="img(webConfig.icon)" alt="">
|
||||
<img class="w-[32x] h-[32px] rounded-full" v-else src="@/app/assets/images/icon-addon.png" alt="">
|
||||
<span class="ml-[10px] text-[16px] font-bold">{{webConfig.site_name}}</span>
|
||||
@ -71,12 +71,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, toRefs } from 'vue'
|
||||
import { reactive, ref, toRefs, computed } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { getHomeSite } from '@/app/api/home'
|
||||
import { getWebConfig } from '@/app/api/sys'
|
||||
import { img } from '@/utils/common'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import storage from '@/utils/storage'
|
||||
import { getInstalledAddonList } from '@/app/api/addon'
|
||||
import { ElMessage } from 'element-plus'
|
||||
@ -138,24 +138,12 @@ const cutAppFn = (app:any) => {
|
||||
}
|
||||
|
||||
// 网络设置
|
||||
const webConfig = ref({
|
||||
icon: '',
|
||||
site_name: ''
|
||||
})
|
||||
const getWebConfigFn = () => {
|
||||
getWebConfig().then(res => {
|
||||
webConfig.value = res.data
|
||||
})
|
||||
}
|
||||
getWebConfigFn()
|
||||
const webConfig = computed(() => useSystemStore().website)
|
||||
|
||||
const selectSite = (site: any) => {
|
||||
storage.set({ key: 'siteId', data: site.site_id })
|
||||
storage.set({ key: 'siteInfo', data: site })
|
||||
storage.set({ key: 'comparisonSiteIdStorage', data: site.site_id })
|
||||
useUserStore().$patch((site) => {
|
||||
site.siteInfo = site
|
||||
})
|
||||
location.href = `${location.origin}/site/`
|
||||
}
|
||||
const logoutFn = () => {
|
||||
|
||||
@ -1,101 +0,0 @@
|
||||
<template>
|
||||
<div class="main-container w-full pt-[64px] bg-white" v-loading="loading">
|
||||
<div class="flex justify-between items-center h-[32px] mb-4">
|
||||
<span class="text-page-title">{{ t('editPersonal') }}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-form :model="saveInfo" label-width="90px" ref="formRef" class="page-form">
|
||||
<el-form-item :label="t('headImg')">
|
||||
<upload-image v-model="saveInfo.head_img" :limit="1" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('userName')">
|
||||
<span>{{saveInfo.username}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('realName')">
|
||||
<el-input v-model="saveInfo.real_name" :placeholder="t('realNamePlaceholder')" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="flex justify-center mt-[50px]">
|
||||
<el-button type="primary" @click="submitForm(formRef)">{{ t('save') }}</el-button>
|
||||
<el-button type="primary" @click="returnFn()">{{ t('cancel') }}</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
|
||||
import { getUserInfo, setUserInfo } from '@/app/api/personal'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
// 提交信息
|
||||
const saveInfo = reactive({
|
||||
head_img: '',
|
||||
real_name: '',
|
||||
username: ''
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(true)
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
const getUserInfoFn = () => {
|
||||
loading.value = true
|
||||
getUserInfo().then(res => {
|
||||
loading.value = false
|
||||
const data = res.data
|
||||
saveInfo.head_img = data.head_img
|
||||
saveInfo.real_name = data.real_name
|
||||
saveInfo.username = data.username
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
getUserInfoFn()
|
||||
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
setUserInfo(saveInfo).then((res: any) => {
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
const returnFn = () => {
|
||||
router.push('/user/center')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.personal-body){
|
||||
background-color: #fff;
|
||||
.el-form-item__content{
|
||||
.el-input{
|
||||
width: 250px;
|
||||
}
|
||||
.el-form-item__content{
|
||||
justify-content: space-between;
|
||||
}
|
||||
.el-button{
|
||||
margin-left: auto;
|
||||
}
|
||||
.personal-option{
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,8 +1,8 @@
|
||||
<!-- eslint-disable no-undef -->
|
||||
<template>
|
||||
<div class="bg-[#FAFAFA] box-border pb-[77px]">
|
||||
<div class="main-container" v-loading="loading">
|
||||
<div class="pt-[60px]">
|
||||
<div v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[24px] font-600 text-[#242424] leading-[33px]">欢迎使用niucloud-admin</span>
|
||||
<div class="ml-[12px] bg-[#333] flex items-center py-[3px] px-[6px] rounded">
|
||||
@ -36,7 +36,6 @@
|
||||
<span class="text-[12px] text-[#666] leading-[16px]">{{ time }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div @click="toHref('site/list','1')" class="cursor-pointer">
|
||||
@ -138,7 +137,6 @@
|
||||
<div ref="siteStat" :style="{ width: '100%', height: '300px' }"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card class="box-card !border-none mt-[15px] site" shadow="never" :body-style="{ marginTop: '13px' }">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
@ -156,11 +154,11 @@
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item :label="t('productionEnvironment')">{{
|
||||
statInfo.system.environment
|
||||
}}</el-descriptions-item>
|
||||
}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -334,9 +332,6 @@ const toUpgrade = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main-container {
|
||||
margin: 0 84px;
|
||||
}
|
||||
|
||||
:deep(.profile-data .el-card__header) {
|
||||
padding: 0 !important;
|
||||
|
||||
@ -1,102 +0,0 @@
|
||||
<template>
|
||||
<div class="main-container w-full pt-[64px] bg-white" v-loading="loading">
|
||||
<div class="flex justify-between items-center h-[32px] mb-4">
|
||||
<span class="text-page-title">{{ t('personal') }}</span>
|
||||
<span class="text-[14px] text-[#999] cursor-pointer" @click="toEditPersonal">{{ t('editPersonal') }}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-form :model="saveInfo" label-width="90px" ref="formRef" class="page-form">
|
||||
<el-form-item :label="t('headImg')">
|
||||
<el-image class="w-[70px] h-[70px]" :src="img(saveInfo.head_img)" fit="contain">
|
||||
<template #error>
|
||||
<div
|
||||
class="image-slot bg-[#c0c4cc] flex items-center justify-center w-[70px] h-[70px] rounded-full">
|
||||
<el-icon class="!text-[#fff] !text-[45px]">
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('userName')">
|
||||
<div>{{ saveInfo.username }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('realName')">
|
||||
<div>{{ saveInfo.real_name }}</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { img } from '@/utils/common'
|
||||
import { getUserInfo } from '@/app/api/personal'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
// 提交信息
|
||||
const saveInfo = reactive({
|
||||
head_img: '',
|
||||
real_name: '',
|
||||
original_password: '',
|
||||
password: '',
|
||||
password_copy: '',
|
||||
username: ''
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(true)
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
const getUserInfoFn = () => {
|
||||
loading.value = true
|
||||
getUserInfo().then(res => {
|
||||
loading.value = false
|
||||
|
||||
const data = res.data
|
||||
saveInfo.head_img = data.head_img
|
||||
saveInfo.real_name = data.real_name
|
||||
saveInfo.original_password = data.original_password
|
||||
saveInfo.password = data.password
|
||||
saveInfo.password_copy = data.password
|
||||
saveInfo.username = data.username
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
getUserInfoFn()
|
||||
|
||||
// 编辑个人中心
|
||||
const toEditPersonal = () => {
|
||||
router.push('/user/edit_center')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.personal-body) {
|
||||
background-color: #fff;
|
||||
|
||||
.el-form-item__content {
|
||||
.el-input {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.personal-option {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}</style>
|
||||
@ -245,7 +245,11 @@ watch(copied, () => {
|
||||
body {
|
||||
background: #edf0f3;
|
||||
}
|
||||
|
||||
.main-container{
|
||||
overflow: inherit !important;
|
||||
border-radius: inherit;
|
||||
background: inherit;
|
||||
}
|
||||
.copy {
|
||||
background: var(--el-color-primary) !important;
|
||||
color: var(--el-color-white) !important;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="pt-[59px] px-[20px] app-store" v-loading="authLoading">
|
||||
<div>
|
||||
<div class="box-border main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center h-[32px] mb-4">
|
||||
<span class="text-page-title text-[#222]">{{ t('localAppText') }}</span>
|
||||
<el-input class="!w-[250px]" :placeholder="t('search')" v-model="search_name" @keyup.enter="query">
|
||||
@ -11,28 +11,28 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="flex mt-[24px] justify-between">
|
||||
<div class="flex my-[10px] justify-between">
|
||||
<div class="flex">
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[#E0E0E0] rounded-full px-[15px] mr-[24px] cursor-pointer bg-[#f8f8f8] hover:bg-[#fff]', { 'text-[#fff] !bg-[#000] border-[#000]': activeName === 'installed' }]"
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[#E0E0E0] rounded-full px-[20px] mr-[24px] cursor-pointer bg-[#f8f8f8] hover:bg-[#fff]', { 'text-[#fff] !bg-[#000] border-[#000]': activeName === 'installed' }]"
|
||||
@click="activeNameTabFn('installed')">
|
||||
{{ t('installLabel') }}
|
||||
</div>
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[#E0E0E0] rounded-full px-[15px] mr-[24px] cursor-pointer bg-[#f8f8f8] hover:bg-[#fff]', { 'text-[#fff] !bg-[#000] border-[#000]': activeName === 'uninstalled' }]"
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[#E0E0E0] rounded-full px-[20px] mr-[24px] cursor-pointer bg-[#f8f8f8] hover:bg-[#fff]', { 'text-[#fff] !bg-[#000] border-[#000]': activeName === 'uninstalled' }]"
|
||||
@click="activeNameTabFn('uninstalled')">
|
||||
{{ t('uninstalledLabel') }}
|
||||
</div>
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[#E0E0E0] rounded-full px-[15px] mr-[24px] cursor-pointer bg-[#f8f8f8] hover:bg-[#fff]', { 'text-[#fff] !bg-[#000] border-[#000]': activeName === 'all' }]"
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[#E0E0E0] rounded-full px-[20px] mr-[24px] cursor-pointer bg-[#f8f8f8] hover:bg-[#fff]', { 'text-[#fff] !bg-[#000] border-[#000]': activeName === 'all' }]"
|
||||
@click="activeNameTabFn('all')">
|
||||
{{ t('buyLabel') }}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['flex items-center text-white text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-primary rounded-full px-[15px] cursor-pointer bg-primary hover:bg-primary']"
|
||||
<div :class="['flex items-center text-white text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-primary rounded-full px-[20px] cursor-pointer bg-primary hover:bg-primary']"
|
||||
@click="handleCloudBuild">
|
||||
{{ t('cloudBuild') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[25px]">
|
||||
<el-table v-if="localList[activeName].length" :data="info[activeName]" size="large" class="pt-[5px]">
|
||||
<div class="min-h-[300px]" v-loading="authLoading">
|
||||
<el-table v-if="localList[activeName].length&&!authLoading" :data="info[activeName]" size="large" class="pt-[5px]">
|
||||
<el-table-column :label="t('appName')" align="left" width="320">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center cursor-pointer" @click = "handleTips">
|
||||
@ -110,7 +110,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty class="mx-auto overview-empty"
|
||||
v-if="!localList.installed.length && !loading && activeName == 'installed'">
|
||||
v-if="!localList.installed.length && !loading && activeName == 'installed'&&!authLoading">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
@ -121,7 +121,7 @@
|
||||
</template>
|
||||
</el-empty>
|
||||
<el-empty class="mx-auto overview-empty"
|
||||
v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled'">
|
||||
v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled'&&!authLoading">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
@ -135,7 +135,7 @@
|
||||
</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<div v-if="!localList.all.length && !loading && !authinfo && activeName == 'all'"
|
||||
<div v-if="!localList.all.length && !loading && !authinfo && activeName == 'all'&&!authLoading"
|
||||
class="mx-auto overview-empty flex flex-col items-center pt-14 pb-6">
|
||||
<div class="mb-[20px] text-sm text-[#888]">检测到当前账号尚未绑定授权,请先绑定授权!</div>
|
||||
<div class="flex flex-1 flex-wrap justify-center relative">
|
||||
@ -161,7 +161,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<el-empty class="mx-auto overview-empty"
|
||||
v-if="!localList.all.length && !loading && authinfo && activeName == 'all'">
|
||||
v-if="!localList.all.length && !loading && authinfo && activeName == 'all'&&!authLoading">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
@ -384,7 +384,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<upgrade ref="upgradeRef" @complete="localListFn"/>
|
||||
@ -855,10 +855,11 @@ const checkAppMange = () => {
|
||||
authLoading.value = true
|
||||
getAuthinfo()
|
||||
.then((res) => {
|
||||
authLoading.value = false
|
||||
if (res.data.data && res.data.data.length != 0) {
|
||||
authinfo.value = res.data.data
|
||||
}
|
||||
authLoading.value = false
|
||||
|
||||
})
|
||||
.catch(() => {
|
||||
authLoading.value = false
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="box-border pt-[64px]">
|
||||
<div class="text-page-title text-[#222] mb-[32px] pl-[14px]">工具管理</div>
|
||||
<div class="flex flex-wrap mt-[28px]">
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/addon')">
|
||||
<div class="box-border main-container pt-[20px]">
|
||||
<div class="text-page-title text-[#222] mb-[16px] pl-[20px]">工具管理</div>
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/addon')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">插件开发</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/addon_develop.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/code')">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/code')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">代码生成</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -20,7 +20,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/code.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/list')">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/list')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">数据字典</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -29,16 +29,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/sys_dict_list.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/update')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">更新缓存</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
更新缓存
|
||||
</div>
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/tools_Update_cache.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/detection')">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/detection')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">环境监测</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -47,7 +38,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/tools_check_environment.png" class="w-[256px] h-[128px] cursor-pointer" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/schedule')">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/schedule')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">计划任务</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -56,7 +47,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/tools_schedule.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/authorize')">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/authorize')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">授权信息</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -65,7 +56,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/app_auth.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/admin_menu')">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/admin_menu')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">平台菜单</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -74,7 +65,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/official_market.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/site_menu')">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="toLink('/admin/tools/site_menu')">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">站点菜单</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -83,7 +74,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/official_market.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="developerDialogVisible = true">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="developerDialogVisible = true">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">开发模式</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
@ -92,7 +83,7 @@
|
||||
</div>
|
||||
<img src="@/app/assets/images/tools/developer.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
<div class="w-[256px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="goRouter">
|
||||
<div class="w-[256px] tools-item-shadow m-[20px] !mr-[0px] !mt-[0px] rounded-[8px] flex flex-col cursor-pointer leading-[1]" @click="goRouter">
|
||||
<div class="flex-1 pt-[18px] pb-[14px] px-[24px] flex flex-col">
|
||||
<span class="text-[16px] text-[#222] font-bold">官方市场</span>
|
||||
<div class="text-[13px] text-[#6D7278] leading-[18px] mt-[8px] truncate">
|
||||
|
||||
@ -127,7 +127,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { t } from '@/lang'
|
||||
@ -135,7 +135,9 @@ import storage from '@/utils/storage'
|
||||
import { getLoginConfig } from '@/app/api/auth'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { setWindowTitle, img, getAppType } from '@/utils/common'
|
||||
import { getWebConfig, getWebCopyright } from '@/app/api/sys'
|
||||
import { getWebCopyright } from '@/app/api/sys'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const loading = ref(false)
|
||||
const imgLoading = ref(false)
|
||||
@ -150,20 +152,17 @@ getWebCopyright().then(({ data }) => {
|
||||
|
||||
route.redirectedFrom && (route.query.redirect = route.redirectedFrom.path)
|
||||
|
||||
const webSite = ref({})
|
||||
const setFormData = async (id: number = 0) => {
|
||||
webSite.value = await (await getWebConfig()).data
|
||||
}
|
||||
setFormData()
|
||||
|
||||
const webSite = computed(() => useSystemStore().website)
|
||||
// 判断登录页面[平台或者站点]
|
||||
const loginType = ref(getAppType())
|
||||
|
||||
if (loginType.value == 'site') {
|
||||
setWindowTitle(t('siteLogin'))
|
||||
} else {
|
||||
setWindowTitle(t('adminLogin'))
|
||||
}
|
||||
watch(() => webSite.value, () => {
|
||||
if (loginType.value == 'site') {
|
||||
setWindowTitle(webSite.value.site_name+'-'+t('siteLogin'))
|
||||
} else {
|
||||
setWindowTitle(webSite.value.site_name+'-'+t('adminLogin'))
|
||||
}
|
||||
})
|
||||
|
||||
// 验证码 - start
|
||||
const verifyRef = ref(null)
|
||||
@ -215,7 +214,11 @@ const loginFn = (data = {}) => {
|
||||
storage.set({ key: 'app_type', data: loginType.value })
|
||||
const { query: { redirect } } = route
|
||||
const path = typeof redirect === 'string' ? redirect : '/'
|
||||
router.push(path)
|
||||
if (loginType.value == 'admin' && Test.empty(res.data.userrole)) {
|
||||
router.push('/home/index')
|
||||
} else {
|
||||
router.push(path)
|
||||
}
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
<el-input v-model.trim="formData.label_name" clearable :placeholder="t('labelNamePlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('memo')">
|
||||
<el-input v-model.trim="formData.memo" type="textarea" rows="4" clearable :placeholder="t('memoPlaceholder')" class="input-width" maxlength="200" />
|
||||
<el-input v-model.trim="formData.memo" type="textarea" rows="4" clearable :placeholder="t('memoPlaceholder')" class="input-width" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('sort')" prop="sort">
|
||||
<el-input v-model.trim="formData.sort" clearable :placeholder="t('sortPlaceholder')" class="input-width" @keyup="filterNumber($event)" />
|
||||
<el-input v-model.trim="formData.sort" clearable maxlength="6" show-word-limit :placeholder="t('sortPlaceholder')" class="input-width" @keyup="filterNumber($event)" />
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
@ -99,12 +99,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="180">
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="100">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center">
|
||||
<el-button type="primary" link @click="detailEvent(row)">{{ t('detail') }}</el-button>
|
||||
<el-button type="primary" link @click="setMemberLable(row)">{{ t('setLable') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row)">{{ t('memberDelete') }}</el-button>
|
||||
<!-- <el-button type="primary" link @click="deleteEvent(row)">{{ t('memberDelete') }}</el-button>-->
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<div class="flex ml-[18px] justify-between items-center mt-[20px]">
|
||||
<div class="main-container bg-[#fff] rounded-[4px]">
|
||||
<div class="flex ml-[18px] justify-between items-center pt-[20px]">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="150px" ref="ruleFormRef" :rules="rules" class="page-form" v-loading="loading">
|
||||
|
||||
@ -1,48 +1,52 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('copyrightEdit') }}</h3>
|
||||
<el-form-item :label="t('logo')">
|
||||
<upload-image v-model="formData.logo" />
|
||||
</el-form-item>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('copyrightEdit') }}</h3>
|
||||
<el-form-item :label="t('logo')">
|
||||
<upload-image v-model="formData.logo" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('companyName')" prop="company_name">
|
||||
<el-input v-model="formData.company_name" :placeholder="t('companyNamePlaceholder')" class="input-width" clearable maxlength="30"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('companyName')" prop="company_name">
|
||||
<el-input v-model="formData.company_name" :placeholder="t('companyNamePlaceholder')" class="input-width" clearable maxlength="30"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('copyrightLink')" >
|
||||
<el-input v-model="formData.copyright_link" :placeholder="t('copyrightLinkPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('copyrightLink')" >
|
||||
<el-input v-model="formData.copyright_link" :placeholder="t('copyrightLinkPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('copyrightDesc')" >
|
||||
<el-input v-model="formData.copyright_desc" type="textarea" rows="4" clearable :placeholder="t('copyrightDescPlaceholder')" class="input-width" maxlength="150" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('copyrightDesc')" >
|
||||
<el-input v-model="formData.copyright_desc" type="textarea" rows="4" clearable :placeholder="t('copyrightDescPlaceholder')" class="input-width" maxlength="150" />
|
||||
</el-form-item>
|
||||
|
||||
</el-card>
|
||||
</el-card>
|
||||
|
||||
<el-card class="box-card !border-none mt-[16px]" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('putOnRecordEdit') }}</h3>
|
||||
<el-card class="box-card !border-none mt-[16px]" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('putOnRecordEdit') }}</h3>
|
||||
|
||||
<el-form-item :label="t('icp')" prop="icp">
|
||||
<el-input v-model="formData.icp" :placeholder="t('icpPlaceholder')" class="input-width" clearable maxlength="20"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('icp')" prop="icp">
|
||||
<el-input v-model="formData.icp" :placeholder="t('icpPlaceholder')" class="input-width" clearable maxlength="20"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('govRecord')" >
|
||||
<el-input v-model="formData.gov_record" :placeholder="t('govRecordPlaceholder')" class="input-width" clearable maxlength="50"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('govRecord')" >
|
||||
<el-input v-model="formData.gov_record" :placeholder="t('govRecordPlaceholder')" class="input-width" clearable maxlength="50"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('govUrl')" >
|
||||
<el-input v-model="formData.gov_url" :placeholder="t('govUrlPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('govUrl')" >
|
||||
<el-input v-model="formData.gov_url" :placeholder="t('govUrlPlaceholder')" class="input-width" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('marketSupervisionUrl')" >
|
||||
<el-input v-model="formData.market_supervision_url" rows="4" clearable :placeholder="t('marketSupervisionUrlPlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
|
||||
</el-card>
|
||||
</el-form>
|
||||
<el-form-item :label="t('marketSupervisionUrl')" >
|
||||
<el-input v-model="formData.market_supervision_url" rows="4" clearable :placeholder="t('marketSupervisionUrlPlaceholder')" class="input-width" />
|
||||
</el-form-item>
|
||||
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
|
||||
@ -56,10 +60,10 @@ import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { setCopyright, getCopyright } from '@/app/api/sys'
|
||||
import { FormInstance, FormRules } from 'element-plus'
|
||||
// import { useRoute } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
// const route = useRoute()
|
||||
// const pageName = route.meta.title
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<div class="flex ml-[18px] justify-between items-center mt-[20px]">
|
||||
<div class="main-container bg-[#fff] rounded-[4px]">
|
||||
<div class="flex ml-[18px] justify-between items-center pt-[20px]">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="150px" ref="ruleFormRef" class="page-form" v-loading="loading">
|
||||
|
||||
@ -1,32 +1,36 @@
|
||||
<template>
|
||||
<div class="main-container" v-loading="noticeTableData.loading">
|
||||
<div class="flex ml-[18px] justify-between items-center mt-[20px]">
|
||||
<div class="main-container bg-[#fff] rounded-[4px]" v-loading="noticeTableData.loading">
|
||||
<div class="flex ml-[18px] justify-between items-center pt-[20px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-base">{{ t('buyerNotice') }}</h3>
|
||||
<div class="flex flex-row flex-wrap m-[-4px]">
|
||||
<el-table :data="noticeTableData.seller" size="large">
|
||||
<el-table :data="noticeTableData.buyer" size="large" :span-method="buyerSpan">
|
||||
<el-table-column prop="addon_name" :label="t('addon')" min-width="120" />
|
||||
<el-table-column prop="name" :label="t('noticeType')" min-width="120" />
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" min-width="300">
|
||||
<template #default="{ row }">
|
||||
<div class="flex">
|
||||
<div class="text-sm mr-1 flex items-center cursor-pointer" v-if="row.sms_type == 1"
|
||||
@click="setNotice(row, 'sms')">
|
||||
<div class="text-sm mr-1 flex items-center cursor-pointer"
|
||||
v-if="row.support_type.indexOf('sms') != -1"
|
||||
@click="setNotice(row, 'sms')">
|
||||
<el-icon class="text-[15px] mr-[3px]" :class="row.is_sms ? 'open' : ''">
|
||||
<SuccessFilled />
|
||||
</el-icon>
|
||||
<span class="ml-0.5">{{ t('sms') }}</span>
|
||||
</div>
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]" v-if="row.wechat_type"
|
||||
@click="setNotice(row, 'wechat')">
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]"
|
||||
v-if="row.support_type.indexOf('wechat') != -1"
|
||||
@click="setNotice(row, 'wechat')">
|
||||
<el-icon class="text-[15px] mr-[3px]" :class="row.is_wechat ? 'open' : ''">
|
||||
<SuccessFilled />
|
||||
</el-icon>
|
||||
<span class="ml-0.5">{{ t('wechat') }}</span>
|
||||
</div>
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]" v-if="row.weapp_type"
|
||||
@click="setNotice(row, 'weapp')">
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]"
|
||||
v-if="row.support_type.indexOf('weapp') != -1"
|
||||
@click="setNotice(row, 'weapp')">
|
||||
<el-icon class="text-[15px] mr-[3px]" :class="row.is_weapp ? 'open' : ''">
|
||||
<SuccessFilled />
|
||||
</el-icon>
|
||||
@ -42,27 +46,31 @@
|
||||
<el-card class="box-card !border-none mt-[16px]" shadow="never">
|
||||
<h3 class="panel-title !text-base">{{ t('sellerNotice') }}</h3>
|
||||
<div class="flex flex-row flex-wrap m-[-4px]">
|
||||
<el-table :data="noticeTableData.buyer" size="large">
|
||||
<el-table :data="noticeTableData.seller" size="large" :span-method="buyerSpan">
|
||||
<el-table-column prop="addon_name" :label="t('addon')" min-width="120" />
|
||||
<el-table-column prop="name" :label="t('noticeType')" min-width="120" />
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" min-width="300">
|
||||
<template #default="{ row }">
|
||||
<div class="flex">
|
||||
<div class="text-sm mr-1 flex items-center cursor-pointer" v-if="row.sms_type == 1"
|
||||
@click="setNotice(row, 'sms')">
|
||||
<div class="text-sm mr-1 flex items-center cursor-pointer"
|
||||
v-if="row.support_type.indexOf('sms') != -1"
|
||||
@click="setNotice(row, 'sms')">
|
||||
<el-icon class="text-[15px] mr-[3px]" :class="row.is_sms ? 'open' : ''">
|
||||
<SuccessFilled />
|
||||
</el-icon>
|
||||
<span class="ml-0.5">{{ t('sms') }}</span>
|
||||
</div>
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]" v-if="row.wechat_type"
|
||||
@click="setNotice(row, 'wechat')">
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]"
|
||||
v-if="row.support_type.indexOf('wechat') != -1"
|
||||
@click="setNotice(row, 'wechat')">
|
||||
<el-icon class="text-[15px] mr-[3px]" :class="row.is_wechat ? 'open' : ''">
|
||||
<SuccessFilled />
|
||||
</el-icon>
|
||||
<span class="ml-0.5">{{ t('wechat') }}</span>
|
||||
</div>
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]" v-if="row.weapp_type"
|
||||
@click="setNotice(row, 'weapp')">
|
||||
<div class="text-sm flex items-center cursor-pointer ml-[20px]"
|
||||
v-if="row.support_type.indexOf('weapp') != -1"
|
||||
@click="setNotice(row, 'weapp')">
|
||||
<el-icon class="text-[15px] mr-[3px]" :class="row.is_weapp ? 'open' : ''">
|
||||
<SuccessFilled />
|
||||
</el-icon>
|
||||
@ -94,9 +102,9 @@ import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
|
||||
const smsDialog: Record<string, any> | null = ref(null)
|
||||
const wechatDialog: Record<string, any> | null = ref(null)
|
||||
const weappDialog: Record<string, any> | null = ref(null)
|
||||
const smsDialog : Record<string, any> | null = ref(null)
|
||||
const wechatDialog : Record<string, any> | null = ref(null)
|
||||
const weappDialog : Record<string, any> | null = ref(null)
|
||||
|
||||
const noticeTableData = reactive({
|
||||
loading: true,
|
||||
@ -109,40 +117,63 @@ const noticeTableData = reactive({
|
||||
*/
|
||||
const loadNoticeList = () => {
|
||||
noticeTableData.loading = true
|
||||
noticeTableData.buyer = []
|
||||
noticeTableData.seller = []
|
||||
|
||||
getNoticeList().then(res => {
|
||||
Object.keys(res.data).forEach(key => {
|
||||
const item = res.data[key]
|
||||
item.sms_type = item.support_type.indexOf('sms') !== -1 ? 1 : 0
|
||||
item.wechat_type = item.support_type.indexOf('wechat') !== -1 ? 1 : 0
|
||||
item.weapp_type = item.support_type.indexOf('weapp') !== -1 ? 1 : 0
|
||||
if (item.receiver_type == 0) {
|
||||
noticeTableData.buyer.push(item)
|
||||
}
|
||||
if (item.receiver_type == 1) {
|
||||
noticeTableData.seller.push(item)
|
||||
noticeTableData.buyer = []
|
||||
noticeTableData.seller = []
|
||||
res.data.forEach(item => {
|
||||
if (item.notice.length) {
|
||||
const buyer = [], seller = []
|
||||
Object.keys(item.notice).forEach((key, index) => {
|
||||
const notice = item.notice[key]
|
||||
notice.addon_name = item.title
|
||||
notice.receiver_type == 1 ? buyer.push(notice) : seller.push(notice)
|
||||
})
|
||||
if (buyer.length) {
|
||||
buyer[0].rowspan = buyer.length
|
||||
noticeTableData.buyer = noticeTableData.buyer.concat(buyer)
|
||||
}
|
||||
if (seller.length) {
|
||||
seller[0].rowspan = seller.length
|
||||
noticeTableData.seller = noticeTableData.seller.concat(seller)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
noticeTableData.loading = false
|
||||
}).catch(() => {
|
||||
}).catch((e) => {
|
||||
console.log(e)
|
||||
noticeTableData.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
const buyerSpan = (row : any) => {
|
||||
if (row.columnIndex === 0) {
|
||||
if (row.row.rowspan) {
|
||||
return {
|
||||
rowspan: row.row.rowspan,
|
||||
colspan: 1
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rowspan: 0,
|
||||
colspan: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadNoticeList()
|
||||
|
||||
const setNotice = (data: any, type: string) => {
|
||||
const setNotice = (data : any, type : string) => {
|
||||
data.type = type
|
||||
eval('data.status=data.is_' + type)
|
||||
eval(type + 'Dialog.value.setFormData(data)')
|
||||
eval(type + 'Dialog.value.showDialog = true;')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>.open {
|
||||
<style lang="scss" scoped>
|
||||
.open {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
@ -150,4 +181,5 @@ const setNotice = (data: any, type: string) => {
|
||||
>div:nth-last-child(1):first-child {
|
||||
width: 100%;
|
||||
}
|
||||
}</style>
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,65 +1,67 @@
|
||||
<template>
|
||||
<el-card class="box-card !border-none" shadow="never" v-loading="payLoading">
|
||||
<!-- 设置支付配置 -->
|
||||
<div class="flex justify-between items-center" v-if="!payLoading">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
<el-button type="primary" @click="isEdit = true" ref="setConfigBtn">{{ t('setConfig') }}</el-button>
|
||||
</div>
|
||||
<el-card class="box-card box-pay-card !border-none mt-[20px]" shadow="never"
|
||||
v-for="(payItems, payKey) in payConfigData" :key="payKey">
|
||||
<div class="flex mb-3">
|
||||
<span class="text-base">{{ payItems.name }}</span>
|
||||
<div class="main-container" v-loading="payLoading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<!-- 设置支付配置 -->
|
||||
<div class="flex justify-between items-center" v-if="!payLoading">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
<el-button type="primary" @click="isEdit = true" ref="setConfigBtn">{{ t('setConfig') }}</el-button>
|
||||
</div>
|
||||
<div class="pay-table">
|
||||
<div
|
||||
class="flex items-center pay-table-head table-bg table-item-pd table-item-border justify-between table-bg">
|
||||
<span class="text-base text-[#999] w-[150px]">{{ t('payType') }}</span>
|
||||
<!-- <span class="text-base font-bold text-[#999] w-[110px]">{{t('settingDefaultPay')}}</span> -->
|
||||
<span class="text-base text-[#999] w-[110px] text-center">{{ t('onState') }}</span>
|
||||
<span class="text-base text-[#999] w-[80px] text-center" v-if="isEdit">{{ t('templateName') }}</span>
|
||||
<el-card class="box-card box-pay-card !border-none mt-[20px]" shadow="never"
|
||||
v-for="(payItems, payKey) in payConfigData" :key="payKey">
|
||||
<div class="flex mb-3">
|
||||
<span class="text-base">{{ payItems.name }}</span>
|
||||
</div>
|
||||
<div ref="fieldBoxRefs" :data-key="payKey">
|
||||
<div v-for="(childrenItem, childrenIndex) in payItems.pay_type" :key="childrenItem.redio_key"
|
||||
class="flex table-item-border table-item-pd justify-between" :id="payKey + '_' + childrenIndex">
|
||||
<div class="table-item-flex w-[150px]">
|
||||
<span v-if="isEdit" class="iconfont icontuodong mr-2 handle cursor-pointer"></span>
|
||||
<div class="flex items-center select-none">
|
||||
<div class="mr-[15px] w-[30px] h-[30px]"><img class="w-[30px]"
|
||||
:src="img(childrenItem.icon)" /></div>
|
||||
<span class="text-base text-[#666]">{{ childrenItem.name }}</span>
|
||||
<div class="pay-table">
|
||||
<div
|
||||
class="flex items-center pay-table-head table-bg table-item-pd table-item-border justify-between table-bg">
|
||||
<span class="text-base text-[#999] w-[150px]">{{ t('payType') }}</span>
|
||||
<!-- <span class="text-base font-bold text-[#999] w-[110px]">{{t('settingDefaultPay')}}</span> -->
|
||||
<span class="text-base text-[#999] w-[110px] text-center">{{ t('onState') }}</span>
|
||||
<span class="text-base text-[#999] w-[80px] text-center" v-if="isEdit">{{ t('templateName') }}</span>
|
||||
</div>
|
||||
<div ref="fieldBoxRefs" :data-key="payKey">
|
||||
<div v-for="(childrenItem, childrenIndex) in payItems.pay_type" :key="childrenItem.redio_key"
|
||||
class="flex table-item-border table-item-pd justify-between" :id="payKey + '_' + childrenIndex">
|
||||
<div class="table-item-flex w-[150px]">
|
||||
<span v-if="isEdit" class="iconfont icontuodong mr-2 handle cursor-pointer"></span>
|
||||
<div class="flex items-center select-none">
|
||||
<div class="mr-[15px] w-[30px] h-[30px]"><img class="w-[30px]"
|
||||
:src="img(childrenItem.icon)" /></div>
|
||||
<span class="text-base text-[#666]">{{ childrenItem.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="table-item-flex w-[110px] select-none">
|
||||
<el-radio v-model="payItems.default_pay_type" :label="childrenItem.redio_key" size="large" @change="settingDefault(childrenItem,payKey)">{{ t('settingDefault') }}</el-radio>
|
||||
</div> -->
|
||||
<div class="table-item-flex w-[110px] justify-center select-none">
|
||||
<el-switch v-if="isEdit" v-model="childrenItem.status" :active-text="t('isEnable')"
|
||||
@change="enablePaymentMode(childrenItem)" />
|
||||
<div v-else>
|
||||
<el-tag v-if="childrenItem.status" class="ml-2" type="success">{{ t('open') }}</el-tag>
|
||||
<el-tag v-else class="ml-2" type="info">{{ t('notOpen') }}</el-tag>
|
||||
<!-- <div class="table-item-flex w-[110px] select-none">
|
||||
<el-radio v-model="payItems.default_pay_type" :label="childrenItem.redio_key" size="large" @change="settingDefault(childrenItem,payKey)">{{ t('settingDefault') }}</el-radio>
|
||||
</div> -->
|
||||
<div class="table-item-flex w-[110px] justify-center select-none">
|
||||
<el-switch v-if="isEdit" v-model="childrenItem.status" :active-text="t('isEnable')"
|
||||
@change="enablePaymentMode(childrenItem)" />
|
||||
<div v-else>
|
||||
<el-tag v-if="childrenItem.status" class="ml-2" type="success">{{ t('open') }}</el-tag>
|
||||
<el-tag v-else class="ml-2" type="info">{{ t('notOpen') }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-item-flex w-[80px] justify-center select-none" v-if="isEdit">
|
||||
<button @click="configPayFn(childrenItem)" class="text-base"
|
||||
v-if="childrenItem.key != 'balancepay'">{{ t('clickConfigure')
|
||||
}}</button>
|
||||
<button v-else>--</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-item-flex w-[80px] justify-center select-none" v-if="isEdit">
|
||||
<button @click="configPayFn(childrenItem)" class="text-base"
|
||||
v-if="childrenItem.key != 'balancepay'">{{ t('clickConfigure')
|
||||
}}</button>
|
||||
<button v-else>--</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<div class="fixed-footer-wrap" v-if="isEdit">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" :loading="loading" @click="cancelFn">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="saveFn(formRef)">{{ t('save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<wechatpay ref="wechatpayDialog" @complete="setConfigInfo" />
|
||||
<alipay ref="alipayDialog" @complete="setConfigInfo" />
|
||||
<offlinepay ref="offlinepayDialog" @complete="setConfigInfo" />
|
||||
</el-card>
|
||||
<div class="fixed-footer-wrap" v-if="isEdit">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" :loading="loading" @click="cancelFn">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="saveFn(formRef)">{{ t('save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<wechatpay ref="wechatpayDialog" @complete="setConfigInfo" />
|
||||
<alipay ref="alipayDialog" @complete="setConfigInfo" />
|
||||
<offlinepay ref="offlinepayDialog" @complete="setConfigInfo" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@ -1,59 +1,63 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('websiteInfo') }}</h3>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('websiteInfo') }}</h3>
|
||||
|
||||
<el-form-item :label="t('siteName')" prop="site_name">
|
||||
<el-input v-model.trim="formData.site_name" :placeholder="t('siteNamePlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('siteName')" prop="site_name">
|
||||
<el-input v-model.trim="formData.site_name" :placeholder="t('siteNamePlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('logo')">
|
||||
<div>
|
||||
<upload-image v-model="formData.logo" />
|
||||
<p class="text-[12px] text-[#a9a9a9]">{{ t('logoPlaceholder') }}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('logo')">
|
||||
<div>
|
||||
<upload-image v-model="formData.logo" />
|
||||
<p class="text-[12px] text-[#a9a9a9]">{{ t('logoPlaceholder') }}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('icon')">
|
||||
<div>
|
||||
<upload-image v-model="formData.icon" />
|
||||
<p class="text-[12px] text-[#a9a9a9]">{{ t('iconPlaceholder') }}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('icon')">
|
||||
<div>
|
||||
<upload-image v-model="formData.icon" />
|
||||
<p class="text-[12px] text-[#a9a9a9]">{{ t('iconPlaceholder') }}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('keywords')">
|
||||
<el-input v-model="formData.keywords" :placeholder="t('keywordsPlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('keywords')">
|
||||
<el-input v-model="formData.keywords" :placeholder="t('keywordsPlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('desc')">
|
||||
<el-input v-model="formData.desc" type="textarea" rows="4" clearable :placeholder="t('descPlaceholder')" class="input-width" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('frontEndInfo') }}</h3>
|
||||
<el-form-item :label="t('frontEndName')">
|
||||
<el-input v-model="formData.front_end_name" :placeholder="t('frontEndNamePlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('frontEndLogo')">
|
||||
<upload-image v-model="formData.front_end_logo" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none" shadow="never" v-if="app_type == 'admin'">
|
||||
<h3 class="panel-title !text-sm">{{ t('serviceInformation') }}</h3>
|
||||
<el-form-item :label="t('contactsTel')">
|
||||
<el-input v-model="formData.tel" :placeholder="t('contactsTelPlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('wechatCode')">
|
||||
<upload-image v-model="formData.wechat_code" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('customerServiceCode')">
|
||||
<upload-image v-model="formData.enterprise_wechat" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-form>
|
||||
<el-form-item :label="t('desc')">
|
||||
<el-input v-model="formData.desc" type="textarea" rows="4" clearable :placeholder="t('descPlaceholder')" class="input-width" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('frontEndInfo') }}</h3>
|
||||
<el-form-item :label="t('frontEndName')">
|
||||
<el-input v-model="formData.front_end_name" :placeholder="t('frontEndNamePlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('frontEndLogo')">
|
||||
<upload-image v-model="formData.front_end_logo" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none" shadow="never" v-if="appType == 'admin'">
|
||||
<h3 class="panel-title !text-sm">{{ t('serviceInformation') }}</h3>
|
||||
<el-form-item :label="t('contactsTel')">
|
||||
<el-input v-model="formData.tel" :placeholder="t('contactsTelPlaceholder')" class="input-width" clearable maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('wechatCode')">
|
||||
<upload-image v-model="formData.wechat_code" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('customerServiceCode')">
|
||||
<upload-image v-model="formData.enterprise_wechat" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
|
||||
@ -67,14 +71,15 @@ import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { setWebsite, getWebsite, getService } from '@/app/api/sys'
|
||||
import { FormInstance, FormRules } from 'element-plus'
|
||||
import storage from '@/utils/storage'
|
||||
import { getAppType } from '@/utils/common'
|
||||
// import { useRoute } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import useSystemStore from '@/stores/modules/system';
|
||||
|
||||
// const route = useRoute()
|
||||
// const pageName = route.meta.title
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const loading = ref(true)
|
||||
const app_type = ref()
|
||||
const appType = ref(getAppType())
|
||||
const formData = reactive<Record<string, string>>({
|
||||
site_name: '',
|
||||
logo: '',
|
||||
@ -107,8 +112,6 @@ const setFormData = async (id: number = 0) => {
|
||||
formData['wechat_code'] = service_data.wechat_code
|
||||
formData['enterprise_wechat'] = service_data.enterprise_wechat
|
||||
formData['tel'] = service_data.tel
|
||||
|
||||
app_type.value = getAppType()
|
||||
loading.value = false
|
||||
}
|
||||
setFormData()
|
||||
@ -137,19 +140,13 @@ const save = async (formEl: FormInstance | undefined) => {
|
||||
|
||||
setWebsite(formData).then(() => {
|
||||
loading.value = false
|
||||
setStore()
|
||||
appType.value == 'admin' ? useSystemStore().getWebsiteInfo() : useUserStore().getSiteInfo()
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setStore = async () => {
|
||||
const data = await (await getWebsite()).data
|
||||
storage.set({ key: 'siteInfo', data })
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<div class="main-container bg-[#fff] rounded-[4px]">
|
||||
<el-alert class="warm-prompt" type="info">
|
||||
<template #default>
|
||||
<div class="flex items-center">
|
||||
|
||||
@ -1,105 +0,0 @@
|
||||
<template>
|
||||
<div class="main-container w-full bg-white" v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-form :model="saveInfo" label-width="80px" ref="formRef" :rules="formRules" class="page-form">
|
||||
<el-form-item :label="t('headImg')">
|
||||
<upload-image v-model="saveInfo.head_img" :limit="1" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('userName')">
|
||||
<span>{{ saveInfo.username }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('realName')">
|
||||
<el-input v-model="saveInfo.real_name" :placeholder="t('realNamePlaceholder')" clearable
|
||||
class="input-width" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" @click="submitForm(formRef)">{{ t('save') }}</el-button>
|
||||
<el-button type="primary" @click="returnFn(formRef)">{{ t('cancel') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
// import { img } from '@/utils/common'
|
||||
import { getUserInfo, setUserInfo } from '@/app/api/personal'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
// 提交信息
|
||||
const saveInfo = reactive({
|
||||
head_img: '',
|
||||
real_name: '',
|
||||
username: ''
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(true)
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
const getUserInfoFn = () => {
|
||||
loading.value = true
|
||||
getUserInfo().then(res => {
|
||||
loading.value = false
|
||||
|
||||
const data = res.data
|
||||
saveInfo.head_img = data.head_img
|
||||
saveInfo.real_name = data.real_name
|
||||
saveInfo.username = data.username
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
getUserInfoFn()
|
||||
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
setUserInfo(saveInfo).then((res: any) => {
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
const returnFn = () => {
|
||||
router.push('/user/center')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.personal-body) {
|
||||
background-color: #fff;
|
||||
|
||||
.el-form-item__content {
|
||||
.el-input {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.personal-option {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}</style>
|
||||
@ -122,7 +122,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('operation')" min-width="250" align="right" fixed="right">
|
||||
<el-table-column :label="t('operation')" min-width="210" align="right" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="openClose(row.status, row.site_id)"
|
||||
v-if="row.status == 1 || row.status == 3">{{ row.status == 1 ? t('closeTxt') : t('openTxt')
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
<template>
|
||||
<div class="main-container w-full bg-white" v-loading="loading">
|
||||
<el-card class="box-card !border-none relative" shadow="never">
|
||||
<el-form :model="saveInfo" label-width="80px" ref="formRef" class="page-form">
|
||||
<el-form-item :label="t('headImg')">
|
||||
<el-image class="w-[70px] h-[70px]" :src="img(saveInfo.head_img)" fit="contain">
|
||||
<template #error>
|
||||
<div
|
||||
class="image-slot bg-[#c0c4cc] flex items-center justify-center w-[70px] h-[70px] rounded-full">
|
||||
<el-icon class="!text-[#fff] !text-[45px]">
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('userName')">
|
||||
<div>{{ saveInfo.username }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('realName')">
|
||||
<div>{{ saveInfo.real_name }}</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span class="text-[14px] text-[#999] absolute top-[25px] right-[20px] cursor-pointer" @click="toEditPersonal">{{
|
||||
t('editPersonal') }}</span>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { img } from '@/utils/common'
|
||||
import { getUserInfo } from '@/app/api/personal'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
// 提交信息
|
||||
const saveInfo = reactive({
|
||||
head_img: '',
|
||||
real_name: '',
|
||||
original_password: '',
|
||||
password: '',
|
||||
password_copy: '',
|
||||
username: ''
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(true)
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
const getUserInfoFn = () => {
|
||||
loading.value = true
|
||||
getUserInfo().then(res => {
|
||||
loading.value = false
|
||||
|
||||
const data = res.data
|
||||
saveInfo.head_img = data.head_img
|
||||
saveInfo.real_name = data.real_name
|
||||
saveInfo.original_password = data.original_password
|
||||
saveInfo.password = data.password
|
||||
saveInfo.password_copy = data.password
|
||||
saveInfo.username = data.username
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
getUserInfoFn()
|
||||
|
||||
// 编辑个人中心
|
||||
const toEditPersonal = () => {
|
||||
router.push('/user/edit_center')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.personal-body) {
|
||||
background-color: #fff;
|
||||
|
||||
.el-form-item__content {
|
||||
.el-input {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.personal-option {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}</style>
|
||||
@ -56,7 +56,7 @@
|
||||
{{ row.last_ip || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="160">
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="detailEvent(row.uid)">{{ t('detail') }}</el-button>
|
||||
</template>
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<div class="main-container attachment-container">
|
||||
|
||||
<el-card class="box-card !border-none full-container" shadow="never">
|
||||
<div class="flex justify-between items-center mb-[16px]">
|
||||
<span class="text-page-title">{{pageName}}</span>
|
||||
</div>
|
||||
<el-tabs v-model="type">
|
||||
<el-tab-pane :label="t(tab)" v-for="(tab, index) in attachmentType" :name="tab" :key="index">
|
||||
<attachment scene="attachment" :type="tab" />
|
||||
@ -14,7 +18,10 @@
|
||||
import { ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import attachment from '@/components/upload-attachment/attachment.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const attachmentType: string[] = ['image', 'video', 'icon']
|
||||
const type = ref(attachmentType[0])
|
||||
|
||||
@ -30,7 +37,7 @@ const type = ref(attachmentType[0])
|
||||
.el-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
<template>
|
||||
<div class="main-container h-[500px] w-full p-5 bg-white" v-loading="loading">
|
||||
<div class="flex flex-wrap px-2 plug-list pb-10">
|
||||
<div class="flex items-center bg-[#F7F8FA] p-3 w-[295px] relative plug-item mr-4 mb-4 cursor-pointer">
|
||||
<div class="flex flex-col ml-2">
|
||||
<span class="text-sm truncate w-[190px]">{{t('refreshMenu')}}</span>
|
||||
<span class="text-xs text-gray-400 mt-1 truncate w-[190px]" :title="t('refreshMenuDesc')">{{t('refreshMenuDesc')}}</span>
|
||||
</div>
|
||||
<span class="plug-item-operate" @click="refreshMenu()">{{t('refresh')}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center bg-[#F7F8FA] p-3 w-[295px] relative plug-item mr-4 mb-4 cursor-pointer">
|
||||
<div class="flex flex-col ml-2">
|
||||
<span class="text-sm truncate w-[190px]">{{t('dataCache')}}</span>
|
||||
<span class="text-xs text-gray-400 mt-1 truncate w-[190px]" :title="t('dataCacheDesc')">{{t('dataCacheDesc')}}</span>
|
||||
</div>
|
||||
<span class="plug-item-operate" @click="schemaCache()">{{t('refresh')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { clearSchemaCache, menuRefresh } from '@/app/api/sys'
|
||||
|
||||
const loading = ref<Boolean>(false)
|
||||
|
||||
// 数据缓存
|
||||
const schemaCache = () => {
|
||||
loading.value = true
|
||||
clearSchemaCache({}).then(res => {
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
// 更新菜单
|
||||
const refreshMenu = () => {
|
||||
loading.value = true
|
||||
menuRefresh({}).then(res => {
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.plug-item{
|
||||
.plug-item-operate{
|
||||
@apply text-xs absolute right-3 cursor-pointer;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -13,7 +13,9 @@
|
||||
</template>
|
||||
<upload-attachment :limit="limit" type="video" @confirm="confirmSelect" v-else>
|
||||
<div class="w-full h-full flex items-center justify-center flex-col">
|
||||
<icon name="iconfont-icon24gf-playCircle" size="25px" color="var(--el-text-color-secondary)"/>
|
||||
<slot name="icon">
|
||||
<icon name="iconfont-icon24gf-playCircle" size="25px" color="var(--el-text-color-secondary)"/>
|
||||
</slot>
|
||||
</div>
|
||||
</upload-attachment>
|
||||
</div>
|
||||
@ -31,7 +33,9 @@
|
||||
<div class="rounded cursor-pointer relative bg-page video-wrap mr-[10px]" :style="style" v-if="videos.data.length < limit">
|
||||
<upload-attachment :limit="limit" type="video" @confirm="confirmSelect">
|
||||
<div class="w-full h-full flex items-center justify-center flex-col">
|
||||
<icon name="iconfont-icon24gf-playCircle" size="25px" color="var(--el-text-color-secondary)"/>
|
||||
<slot name="icon">
|
||||
<icon name="iconfont-icon24gf-playCircle" size="25px" color="var(--el-text-color-secondary)"/>
|
||||
</slot>
|
||||
</div>
|
||||
</upload-attachment>
|
||||
</div>
|
||||
|
||||
@ -24,6 +24,9 @@
|
||||
"userName": "用户名",
|
||||
"headImg": "头像",
|
||||
"accountNumber": "账号",
|
||||
"accountSettings": "账号设置",
|
||||
"realName":"名称",
|
||||
"realNamePlaceholder": "请输入用户名称",
|
||||
"password": "密码",
|
||||
"confirmPassword": "确认密码",
|
||||
"image": "图片",
|
||||
@ -49,6 +52,7 @@
|
||||
"selectimage": "选择图片",
|
||||
"selectvideo": "选择视频",
|
||||
"selecticon": "选择图标",
|
||||
"selectnews": "选择图文",
|
||||
"uploadimage": "上传图片",
|
||||
"uploadvideo": "上传视频",
|
||||
"addAttachmentCategory": "添加分组",
|
||||
@ -64,7 +68,8 @@
|
||||
"placeholdervideoName": "请输入视频名称",
|
||||
"placeholdericonName": "请输入图标名称",
|
||||
"success": "上传成功",
|
||||
"triggerUpperLimit": "可选数量已达上限"
|
||||
"triggerUpperLimit": "可选数量已达上限",
|
||||
"mediaEmpty": "暂无素材,请点击上传按钮上传"
|
||||
},
|
||||
"tabs": {
|
||||
"closeLeft": "关闭左侧",
|
||||
|
||||
@ -1,101 +1,183 @@
|
||||
<template>
|
||||
<el-aside :class="['layout-aside w-full ease-in duration-200', { 'bright': !dark }]">
|
||||
|
||||
<div class="flex flex-wrap items-center pt-[20px] pb-[10px] pl-[80px] pr-[70px]"
|
||||
v-if="twoMenuData.length">
|
||||
<template v-for="(item,index) in twoMenuData" :key="index">
|
||||
<div
|
||||
v-if="item.meta.show"
|
||||
@click="redirect(item)"
|
||||
:class="['flex items-center h-[32px] border-[1px] border-solid my-[3px] border-[#E0E0E0] rounded-full px-[10px] mr-[24px] cursor-pointer bg-[#f8f8f8] hover:bg-[#fff]',{'text-[#fff] !bg-[#000] border-[#000]': menuActive == item.name || menuTwoActive && menuTwoActive == item.name}]">
|
||||
<icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto mr-[4px] !leading-[14px]" size="14px"
|
||||
:title="item.meta.title" />
|
||||
<span class="text-[14px]">{{ item.meta.short_title || item.meta.title }}</span>
|
||||
<div :class="['layout-aside ease-in duration-200 flex h-full', { 'bright': !dark }]">
|
||||
<div class="flex flex-col h-full border-0 border-r-[1px] border-solid border-[#eee] box-border bg-[#f5f6f8]">
|
||||
<div class="w-full h-[64px] flex justify-center items-center w-[65px]flex-shrink-0">
|
||||
<el-image style="width: 40px; height: 40px" :src="img(logoUrl)" fit="contain">
|
||||
<template #error>
|
||||
<div class="flex justify-center items-center w-full h-[40px]"><img class="max-w-[40px]" src="@/app/assets/images/site_login_logo.png" alt="" object-fit="contain"></div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<el-scrollbar class="flex-1 w-[65px] one-menu">
|
||||
<div class="flex flex-col items-center">
|
||||
<template v-for="(item, index) in oneMenuData">
|
||||
<div v-if="item.meta.show" class="menu-item py-[10px] w-full box-border cursor-pointer" :class="{'is-active':oneMenuActive===item.original_name}" @click="router.push({ name: item.name })">
|
||||
<div class="w-[50px] h-full flex items-center justify-center mx-auto">
|
||||
<template v-if="item.meta.icon">
|
||||
<el-image class="w-[25px] h-[25px] overflow-hidden" :src="item.meta.icon" fit="fill" v-if="isUrl(item.meta.icon)"/>
|
||||
<icon :name="item.meta.icon" size="25px" v-else />
|
||||
</template>
|
||||
<icon v-else :name="'iconfont iconshezhi1'" />
|
||||
</div>
|
||||
<div class="text-center text-[13px] mt-[5px]">{{ item.meta.short_title || item.meta.title }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-scrollbar>
|
||||
|
||||
</div>
|
||||
<!-- 三级菜单 -->
|
||||
<div class="pt-[20px] pl-[80px] pr-[70px]" v-if="threeMenuData.length">
|
||||
<el-tabs v-model="menuTwoActive" @tab-click="toolsHandleClick">
|
||||
<template v-for="(item,index) in threeMenuData" :key="index">
|
||||
<el-tab-pane :label="item.meta.title" :name="item.name" :path="item.path" v-if="item.meta.show"
|
||||
@click="settingMenuFn(item)"></el-tab-pane>
|
||||
</template>
|
||||
</el-tabs>
|
||||
<div class="flex flex-col two-menu w-[140px] h-[100vh]" v-if="twoMenuData.length">
|
||||
<div class="w-[140px] h-[64px] bg-[#fff] flex items-center justify-center text-[16px]">{{ route.matched[1].meta.title }}</div>
|
||||
<el-scrollbar class="flex-1">
|
||||
<el-menu :default-active="route.name" :router="true" class="aside-menu " :collapse="systemStore.menuIsCollapse">
|
||||
<menu-item v-for="(route, index) in twoMenuData" :routes="route" :key="index" />
|
||||
</el-menu>
|
||||
<div class="h-[48px]"></div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-aside>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, ref, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { ADMIN_ROUTE,findFirstValidRoute } from "@/router/routers"
|
||||
import { img, isUrl } from '@/utils/common'
|
||||
import { getWebsite } from '@/app/api/sys'
|
||||
import menuItem from './menu-item.vue'
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
const routers = userStore.routers
|
||||
const systemStore = useSystemStore()
|
||||
const router = useRouter()
|
||||
const dark = computed(() => {
|
||||
return systemStore.dark
|
||||
})
|
||||
|
||||
const logoUrl = computed(() => {
|
||||
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
||||
})
|
||||
const twoMenuData = ref<Record<string, any>[]>([])
|
||||
const threeMenuData = ref<Record<string, any>[]>([])
|
||||
const menuActive = ref('')
|
||||
const menuTwoActive = ref('')
|
||||
|
||||
const oneMenuData = ref<Record<string, any>[]>([])
|
||||
routers.forEach(item => {
|
||||
item.original_name = item.name
|
||||
if (item.children && item.children.length) {
|
||||
item.name = findFirstValidRoute(item.children)
|
||||
}
|
||||
oneMenuData.value.push(item)
|
||||
})
|
||||
|
||||
const oneMenuActive = ref(oneMenuData.value[0].name)
|
||||
watch(route, () => {
|
||||
twoMenuData.value = route.matched[1].children ?? []
|
||||
threeMenuData.value = route.matched[2].children ?? []
|
||||
menuActive.value = route.matched[2] ? route.matched[2].name : ''
|
||||
menuTwoActive.value = route.matched[3] ? route.matched[3].name : ''
|
||||
oneMenuActive.value = route.matched[1].name == ADMIN_ROUTE.children[0].name ? route.matched[2].name : route.matched[1].name
|
||||
}, { immediate: true })
|
||||
|
||||
/**
|
||||
* 跳转
|
||||
* @param data
|
||||
*/
|
||||
const redirect = (data: any) => {
|
||||
if (data.children) {
|
||||
router.push(data.children[0].path)
|
||||
} else {
|
||||
router.push(data.path)
|
||||
}
|
||||
}
|
||||
|
||||
const toolsHandleClick = (tab, event: Event) => {
|
||||
router.push({name: tab.props.name})
|
||||
}
|
||||
// const logoShow = ref<boolean>(false)
|
||||
// const getWebsiteFn = ()=>{
|
||||
// getWebsite().then((res:any)=>{
|
||||
// logoUrl.value = res.data.icon
|
||||
// console.log(res)
|
||||
// logoShow.value = true
|
||||
// }).catch(()=>{
|
||||
// logoShow.value = true
|
||||
// })
|
||||
// }
|
||||
// getWebsiteFn()
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.layout-aside {
|
||||
&.bright {
|
||||
li {
|
||||
background-color: #F5F7F9;
|
||||
|
||||
&.is-active:not(.is-opened) {
|
||||
position: relative;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
background-color: var(--el-menu-active-color);
|
||||
.one-menu{
|
||||
.menu-item{
|
||||
&:hover{
|
||||
color:var(--el-color-primary);
|
||||
}
|
||||
&.is-active{
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
span{
|
||||
font-size: 14px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
.el-menu{
|
||||
border: 0;
|
||||
}
|
||||
.el-scrollbar{
|
||||
height: calc(100vh - 65px);
|
||||
}
|
||||
}
|
||||
.two-menu{
|
||||
.aside-menu:not(.el-menu--collapse) {
|
||||
width: 140px;
|
||||
border: 0;
|
||||
padding-top: 10px;
|
||||
.el-menu-item{
|
||||
height: 36px;
|
||||
margin: 0 8px 4px;
|
||||
padding: 0 8px !important;
|
||||
border-radius: 2px;
|
||||
span{
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
&.is-active{
|
||||
background-color: var(--el-color-primary-light-9) !important;
|
||||
}
|
||||
&:hover{
|
||||
background-color: #f7f7f7;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.el-sub-menu{
|
||||
margin-bottom: 8px;
|
||||
.el-sub-menu__title{
|
||||
margin: 0 8px 4px;
|
||||
height: 36px;
|
||||
padding-left: 8px;
|
||||
border-radius: 2px;
|
||||
span{
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
&:hover{
|
||||
background-color: #f7f7f7;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.el-icon.el-sub-menu__icon-arrow{
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
.el-menu-item{
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item:hover{
|
||||
color: var(--el-color-primary);
|
||||
.logo-wrap {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
|
||||
.logo {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-size: var(--el-font-size-base);
|
||||
}
|
||||
}
|
||||
.text-color{
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
</div>
|
||||
<span :class="['ml-[10px]', {'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}]">{{ meta.title }}</span>
|
||||
</template>
|
||||
<menu-item v-for="(route, index) in routes.children" :routes="route" :route-path="resolvePath(route.path)" :key="index" />
|
||||
<menu-item v-for="(route, index) in routes.children" :routes="route" :key="index" />
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="routes.meta.class == 1" :index="String(routes.name)" :route="'/'+routePath">
|
||||
<el-menu-item v-else-if="routes.meta.class == 1" :index="String(routes.name)" :route="routes.path">
|
||||
<div v-if="meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
||||
<icon :name="meta.icon" class="absolute top-[50%] -translate-y-[50%]" />
|
||||
</div>
|
||||
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item v-else :index="String(routes.name)" :route="'/'+routePath">
|
||||
<el-menu-item v-else :index="String(routes.name)" :route="routes.path">
|
||||
<template #title>
|
||||
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
||||
</template>
|
||||
@ -62,16 +62,16 @@ const props = defineProps({
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
routePath: {
|
||||
type: String
|
||||
}
|
||||
// routePath: {
|
||||
// type: String
|
||||
// }
|
||||
})
|
||||
|
||||
const meta = computed(() => props.routes.meta)
|
||||
|
||||
const resolvePath = (path: string) => {
|
||||
return `${props.routePath}/${path}`
|
||||
}
|
||||
// const resolvePath = (path: string) => {
|
||||
// return `/${path}`
|
||||
// }
|
||||
|
||||
const indexList = ref();
|
||||
const showDialog = ref(false)
|
||||
|
||||
@ -1,23 +1,20 @@
|
||||
<template>
|
||||
<el-container class="h-[60px] bg-[#2B303B] layout-admin flex items-center justify-between px-[15px] text-white" >
|
||||
<el-container class="h-[64px] layout-admin flex items-center justify-between px-[15px]" >
|
||||
<!-- :class="['h-full px-[10px]',{'layout-header border-b border-color': !dark}]" -->
|
||||
<div class="flex items-center text-[14px] leading-[1]">
|
||||
<span class="iconfont icontuodong !text-[25px] mr-[6px]"></span>
|
||||
|
||||
<template v-for="(item, index) in oneMenuData">
|
||||
<template v-if="item.meta.show">
|
||||
<span class="mx-2 text-[#4F5563] mx-[15px]" v-if="index">|</span>
|
||||
<span class="cursor-pointer" @click="router.push({ name: item.name })" >
|
||||
{{ item.meta.short_title || item.meta.title }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div class="right-panel h-full flex items-center justify-end">
|
||||
<div class="left-panel flex items-center text-[14px] leading-[1]">
|
||||
<!-- 刷新当前页 -->
|
||||
<div class="navbar-item flex items-center h-full cursor-pointer" @click="refreshRouter">
|
||||
<icon name="element-Refresh" />
|
||||
</div>
|
||||
<!-- 面包屑导航 -->
|
||||
<div class="flex items-center h-full pl-[10px] hidden-xs-only">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item v-for="(route, index) in breadcrumb" :key="index">{{route.meta.title }}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-panel h-full flex items-center justify-end">
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<div class="navbar-item flex items-center h-full cursor-pointer">
|
||||
<user-info />
|
||||
@ -58,29 +55,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref,computed } from 'vue'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRoute,useRouter } from 'vue-router'
|
||||
import { t } from '@/lang'
|
||||
import storage from '@/utils/storage'
|
||||
import userInfo from './user-info.vue'
|
||||
import { findFirstValidRoute } from "@/router/routers"
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const appStore = useAppStore()
|
||||
const userStore = useUserStore()
|
||||
const routers = userStore.routers
|
||||
|
||||
const oneMenuData = ref<Record<string, any>[]>([])
|
||||
routers.forEach(item => {
|
||||
item.original_name = item.name
|
||||
if (item.children && item.children.length) {
|
||||
item.name = findFirstValidRoute(item.children)
|
||||
}
|
||||
oneMenuData.value.push(item)
|
||||
})
|
||||
|
||||
// 检测登录 start
|
||||
const detectionLoginDialog = ref(false)
|
||||
const comparisonToken = ref('')
|
||||
@ -110,6 +97,12 @@ const refreshRouter = () => {
|
||||
appStore.refreshRouterView()
|
||||
}
|
||||
|
||||
// 面包屑导航
|
||||
const breadcrumb = computed(() => {
|
||||
const matched = route.matched.filter(item => { return item.meta.title })
|
||||
if (matched[0] && matched[0].path == '/') matched.splice(0, 1)
|
||||
return matched
|
||||
})
|
||||
storage.set({ key: 'currHeadMenuName', data: "" })
|
||||
</script>
|
||||
|
||||
@ -121,7 +114,6 @@ storage.set({ key: 'currHeadMenuName', data: "" })
|
||||
}
|
||||
.navbar-item {
|
||||
padding: 0 8px;
|
||||
color: #fff;
|
||||
}
|
||||
.index-item {
|
||||
border: 1px solid;
|
||||
@ -133,7 +125,7 @@ storage.set({ key: 'currHeadMenuName', data: "" })
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.layout-admin .el-dropdown{
|
||||
/* .layout-admin .el-dropdown{
|
||||
color: #fff;
|
||||
}
|
||||
} */
|
||||
</style>
|
||||
|
||||
@ -8,13 +8,13 @@
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<router-link to="/user/center">
|
||||
<el-dropdown-item @click="getUserInfoFn">
|
||||
<!-- <router-link to="/user/center"> -->
|
||||
<div class="flex items-center leading-[1] py-[5px]">
|
||||
<span class="iconfont iconshezhi1 ml-[4px] !text-[14px] mr-[10px]"></span>
|
||||
<span class="text-[14px]">账号设置</span>
|
||||
</div>
|
||||
</router-link>
|
||||
<!-- </router-link> -->
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<router-link to="/tools/authorize">
|
||||
@ -61,6 +61,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<user-info-edit ref="userInfoEditRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -72,7 +73,7 @@ import type { FormInstance, FormRules, ElNotification } from 'element-plus'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { setUserInfo } from '@/app/api/personal'
|
||||
import { t } from '@/lang'
|
||||
|
||||
import userInfoEdit from '@/app/components/user-info-edit/index.vue'
|
||||
const userStore = useUserStore()
|
||||
|
||||
const clickEvent = (command: string) => {
|
||||
@ -86,7 +87,10 @@ const clickEvent = (command: string) => {
|
||||
const logout = () => {
|
||||
userStore.logout();
|
||||
}
|
||||
|
||||
const userInfoEditRef = ref(null)
|
||||
const getUserInfoFn = ()=>{
|
||||
userInfoEditRef.value?.open()
|
||||
}
|
||||
// 修改密码 --- start
|
||||
let changePasswordDialog = ref(false)
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
@ -1,21 +1,23 @@
|
||||
|
||||
<template>
|
||||
<div class="common-layout min-w-[1200px]" >
|
||||
<el-container class="w-100 h-screen">
|
||||
<el-header class="h-[60px]">
|
||||
<layout-header></layout-header>
|
||||
</el-header>
|
||||
<el-container class="flex flex-col">
|
||||
<div ref="layoutAsideRef">
|
||||
<layout-aside @complete="heightChange"></layout-aside>
|
||||
</div>
|
||||
<layout-aside></layout-aside>
|
||||
|
||||
<el-main :class="['main-wrap h-full p-0',{'bg-page': dark}]">
|
||||
<el-scrollbar class="layout-content-height" :class="{'px-[64px]': flag }" :style="{height: contentHeight}">
|
||||
<router-view v-slot="{ Component, route }" v-if="appStore.routeRefreshTag">
|
||||
<keep-alive :include="tabbarStore.tabNames">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
<el-container>
|
||||
<el-header class="!h-[64px]">
|
||||
<layout-header></layout-header>
|
||||
</el-header>
|
||||
|
||||
<el-main :class="['main-wrap h-full p-0 bg-page']">
|
||||
<el-scrollbar>
|
||||
<div class="p-[10px]">
|
||||
<router-view v-slot="{ Component, route }" v-if="appStore.routeRefreshTag">
|
||||
<keep-alive :include="tabbarStore.tabNames">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
|
||||
@ -23,40 +25,15 @@
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, onMounted} from 'vue'
|
||||
import layoutHeader from './components/header/index.vue'
|
||||
import layoutAside from './components/aside/index.vue'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import useTabbarStore from '@/stores/modules/tabbar'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import useStyleStore from '@/stores/modules/style'
|
||||
import { CollectionTag } from '@element-plus/icons-vue'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const tabbarStore = useTabbarStore()
|
||||
const systemStore = useSystemStore()
|
||||
const layoutAsideRef = ref()
|
||||
const styleStore = useStyleStore()
|
||||
|
||||
// 控制页面内容高度start
|
||||
let contentHeight= ref("")
|
||||
const heightChange = ()=>{
|
||||
setTimeout(() => {
|
||||
contentHeight.value = `calc(100vh - ${layoutAsideRef.value.clientHeight + 60}px)`
|
||||
}, 600);
|
||||
}
|
||||
heightChange();
|
||||
// 控制页面内容高度end
|
||||
|
||||
const dark = computed(()=>{
|
||||
return systemStore.dark
|
||||
})
|
||||
|
||||
const flag = computed(()=>{
|
||||
return styleStore.flag
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -4,8 +4,11 @@
|
||||
<div class="w-[124px] px-[8px] bg-[#282c34] h-screen one-menu">
|
||||
<el-header class="logo-wrap">
|
||||
<div class="logo flex items-center m-auto h-[64px]" v-if="!systemStore.menuIsCollapse">
|
||||
<img class="max-h-[40px] max-w-[40px] rounded-full" v-if="siteInfo.logo" :src="img(siteInfo.logo)" alt="">
|
||||
<img class="max-h-[40px] max-w-[40px] rounded-full" v-else src="@/app/assets/images/icon-addon.png" alt="">
|
||||
<el-image style="width: 40px; height: 40px" :src="img(logoUrl)" fit="contain">
|
||||
<template #error>
|
||||
<div class="flex justify-center items-center w-full h-[40px]"><img class="max-w-[40px]" src="@/app/assets/images/icon-addon.png" alt="" object-fit="contain"></div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="logo flex items-center justify-center h-[64px]" v-else>
|
||||
<i class="text-3xl iconfont iconyunkongjian"></i>
|
||||
@ -43,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { ref, watch,computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
@ -62,6 +65,9 @@ const addonIndexRoute = userStore.addonIndexRoute
|
||||
const oneMenuData = ref<Record<string, any>[]>([])
|
||||
const twoMenuData = ref<Record<string, any>[]>([])
|
||||
const addonRouters: Record<string, any> = {}
|
||||
const logoUrl = computed(() => {
|
||||
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
||||
})
|
||||
|
||||
routers.forEach(item => {
|
||||
item.original_name = item.name
|
||||
@ -142,12 +148,12 @@ watch(route, () => {
|
||||
font-size: 14px;
|
||||
border-radius: 2px;
|
||||
&:hover{
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
&.is-active{
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff;
|
||||
color: #fff !important;
|
||||
}
|
||||
span{
|
||||
font-size: 14px;
|
||||
|
||||
@ -14,11 +14,13 @@
|
||||
<span class="text-[14px]">切换站点</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="toLink('/user/center')">
|
||||
<div class="flex items-center leading-[1] py-[5px]">
|
||||
<span class="iconfont iconshezhi1 ml-[4px] !text-[14px] mr-[10px]"></span>
|
||||
<span class="text-[14px]">账号设置</span>
|
||||
</div>
|
||||
<el-dropdown-item @click="getUserInfoFn">
|
||||
<!-- <router-link to="/user/center"> -->
|
||||
<div class="flex items-center leading-[1] py-[5px]">
|
||||
<span class="iconfont iconshezhi1 ml-[4px] !text-[14px] mr-[10px]"></span>
|
||||
<span class="text-[14px]">账号设置</span>
|
||||
</div>
|
||||
<!-- </router-link> -->
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="changePasswordDialog=true">
|
||||
<div class="flex items-center leading-[1] py-[5px]">
|
||||
@ -26,16 +28,15 @@
|
||||
<span class="text-[14px]">修改密码</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="logout">
|
||||
<div class="flex items-center leading-[1] py-[2px]">
|
||||
<span class="iconfont icontuichudenglu !text-[21px] mr-[8px]"></span>
|
||||
<el-dropdown-item @click="logout">
|
||||
<div class="flex items-center leading-[1] py-[5px]">
|
||||
<span class="iconfont icontuichudenglu ml-[4px] !text-[14px] mr-[10px]"></span>
|
||||
<span class="text-[14px]">退出登录</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<el-dialog v-model="changePasswordDialog" width="450px" title="修改密码" :before-close="handleClose">
|
||||
<div>
|
||||
<el-form :model="saveInfo" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
|
||||
@ -58,21 +59,21 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<user-info-edit ref="userInfoEditRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { UserFilled } from '@element-plus/icons-vue'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { computed, reactive, ref, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import type { FormInstance, FormRules, ElNotification } from 'element-plus'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { setUserInfo } from '@/app/api/personal'
|
||||
import { t } from '@/lang'
|
||||
|
||||
import userInfoEdit from '@/app/components/user-info-edit/index.vue'
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
const clickEvent = (command: string) => {
|
||||
switch (command) {
|
||||
case 'logout':
|
||||
@ -81,54 +82,60 @@ const clickEvent = (command: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
userStore.logout();
|
||||
}
|
||||
const toLink = (link) => {
|
||||
router.push(link)
|
||||
}
|
||||
|
||||
const userInfoEditRef = ref(null)
|
||||
const getUserInfoFn = ()=>{
|
||||
userInfoEditRef.value?.open()
|
||||
}
|
||||
// 修改密码 --- start
|
||||
const changePasswordDialog = ref(false)
|
||||
const formRef = ref<FormInstance>()
|
||||
let changePasswordDialog = ref(false)
|
||||
const formRef = ref<FormInstance>();
|
||||
// 提交信息
|
||||
const saveInfo = reactive({
|
||||
let saveInfo = reactive({
|
||||
original_password: '',
|
||||
password: '',
|
||||
password_copy: ''
|
||||
})
|
||||
});
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
original_password: [
|
||||
{ required: true, message: t('originalPasswordPlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: t('passwordPlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
password_copy: [
|
||||
{ required: true, message: t('passwordPlaceholder'), trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
original_password: [
|
||||
{ required: true, message: t("originalPasswordPlaceholder"), trigger: "blur" },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: t("passwordPlaceholder"), trigger: "blur" },
|
||||
],
|
||||
password_copy: [
|
||||
{ required: true, message: t("passwordPlaceholder"), trigger: "blur" },
|
||||
]
|
||||
});
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
let msg = ''
|
||||
if (saveInfo.password && !saveInfo.original_password) msg = t('originalPasswordHint')
|
||||
if (saveInfo.password && saveInfo.original_password && !saveInfo.password_copy) msg = t('newPasswordHint')
|
||||
if (saveInfo.password && saveInfo.original_password && saveInfo.password_copy && saveInfo.password != saveInfo.password_copy) msg = t('doubleCipherHint')
|
||||
let msg = "";
|
||||
if (saveInfo.password && !saveInfo.original_password) msg = t('originalPasswordHint');
|
||||
if (saveInfo.password && saveInfo.original_password && !saveInfo.password_copy) msg = t('newPasswordHint');
|
||||
if (saveInfo.password && saveInfo.original_password && saveInfo.password_copy && saveInfo.password != saveInfo.password_copy) msg = t('doubleCipherHint');
|
||||
if (msg) {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: msg
|
||||
message: msg,
|
||||
})
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
setUserInfo(saveInfo).then((res: any) => {
|
||||
changePasswordDialog.value = false
|
||||
changePasswordDialog.value = false;
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
// 修改密码 --- end
|
||||
</script>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<layout-header></layout-header>
|
||||
</el-header>
|
||||
|
||||
<el-main :class="['main-wrap h-full p-0',{'bg-page': dark}]">
|
||||
<el-main :class="['main-wrap h-full p-0 bg-page']">
|
||||
<el-scrollbar>
|
||||
<div class="p-[10px]">
|
||||
<router-view v-slot="{ Component, route }" v-if="appStore.routeRefreshTag">
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<layout-header></layout-header>
|
||||
</el-header>
|
||||
|
||||
<el-main class="main-wrap h-full p-0">
|
||||
<el-main class="main-wrap h-full p-0 bg-page">
|
||||
<el-scrollbar>
|
||||
<div class="p-[10px]">
|
||||
<router-view v-slot="{ Component, route }" v-if="appStore.routeRefreshTag">
|
||||
|
||||
@ -47,27 +47,19 @@ router.beforeEach(async (to, from, next) => {
|
||||
to.redirectedFrom && (to.query = to.redirectedFrom.query)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const siteInfo = userStore.siteInfo
|
||||
const systemStore = useSystemStore()
|
||||
const appType = getAppType()
|
||||
|
||||
let title: string = to.meta.title ?? ''
|
||||
|
||||
if (!siteInfo && appType != 'home') {
|
||||
if (!userStore.siteInfo && appType != 'home') {
|
||||
await userStore.getSiteInfo()
|
||||
}
|
||||
|
||||
if (siteInfo) {
|
||||
title += '-' + siteInfo.site_name
|
||||
}
|
||||
|
||||
// 设置网站标题
|
||||
setWindowTitle(title)
|
||||
|
||||
// 加载语言包
|
||||
await language.loadLocaleMessages(to.meta.addon || '', (to.meta.view || to.path), useSystemStore().lang);
|
||||
await language.loadLocaleMessages(to.meta.addon || '', (to.meta.view || to.path), systemStore.lang);
|
||||
|
||||
let matched: any = to.matched;
|
||||
|
||||
if (matched && matched.length && matched[0].path != '/:pathMatch(.*)*') {
|
||||
matched = matched[0].path;
|
||||
} else {
|
||||
@ -76,6 +68,15 @@ router.beforeEach(async (to, from, next) => {
|
||||
|
||||
const loginPath = to.path == '/' ? '/admin/login' : `/${matched == '/admin' ? 'admin' : 'site'}/login`
|
||||
|
||||
if (appType != 'site' || to.path === loginPath) {
|
||||
title += systemStore.website.site_name ? ('-' + systemStore.website.site_name) : ''
|
||||
} else {
|
||||
title += userStore.siteInfo && userStore.siteInfo.site_name ? ('-' + userStore.siteInfo.site_name) : ''
|
||||
}
|
||||
|
||||
// 设置网站标题
|
||||
setWindowTitle(title)
|
||||
|
||||
// 判断是否需登录
|
||||
if (NO_LOGIN_ROUTES.includes(to.path)) {
|
||||
next()
|
||||
|
||||
@ -36,34 +36,6 @@ export const ADMIN_ROUTE: RouteRecordRaw = {
|
||||
{
|
||||
path: 'login',
|
||||
component: () => import('@/app/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
component: Default,
|
||||
children: [
|
||||
{
|
||||
path: 'center',
|
||||
meta: {
|
||||
type: 1,
|
||||
title: '个人中心'
|
||||
},
|
||||
component: () => import('@/app/views/index/personal.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
component: Default,
|
||||
children: [
|
||||
{
|
||||
path: 'edit_center',
|
||||
meta: {
|
||||
type: 1,
|
||||
title: '编辑个人中心'
|
||||
},
|
||||
component: () => import('@/app/views/index/edit_personal.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -103,34 +75,6 @@ export const SITE_ROUTE: RouteRecordRaw = {
|
||||
{
|
||||
path: 'login',
|
||||
component: () => import('@/app/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
component: Default,
|
||||
children: [
|
||||
{
|
||||
path: 'center',
|
||||
meta: {
|
||||
type: 1,
|
||||
title: '个人中心'
|
||||
},
|
||||
component: () => import('@/app/views/site/personal.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
component: Default,
|
||||
children: [
|
||||
{
|
||||
path: 'edit_center',
|
||||
meta: {
|
||||
type: 1,
|
||||
title: '编辑个人中心'
|
||||
},
|
||||
component: () => import('@/app/views/site/edit_personal.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ const useDiyStore = defineStore('diy', {
|
||||
'#c7158577'
|
||||
],
|
||||
components: [], // 组件集合
|
||||
position: ['fixed', 'top_fixed','right_fixed','bottom_fixed','left_fixed'],
|
||||
position: ['top_fixed','right_fixed','bottom_fixed','left_fixed','fixed'],
|
||||
global: {
|
||||
title: "页面", // 页面标题
|
||||
|
||||
@ -211,7 +211,14 @@ const useDiyStore = defineStore('diy', {
|
||||
delete component.template;
|
||||
}
|
||||
|
||||
if (!this.checkComponentIsAdd(component)) return;
|
||||
if (!this.checkComponentIsAdd(component)) {
|
||||
// 组件最多只能添加n个
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: `${component.componentTitle}${t('componentCanOnlyAdd')}${component.uses}${t('piece')}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 置顶组件,只能在第一个位置中添加
|
||||
if(component.position && this.position.indexOf(component.position) != -1){
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import storage from '@/utils/storage'
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
import { getWebConfig } from '@/app/api/sys'
|
||||
|
||||
interface System {
|
||||
menuIsCollapse: boolean,
|
||||
@ -9,7 +10,9 @@ interface System {
|
||||
theme: string,
|
||||
lang: string,
|
||||
sidebar: string,
|
||||
sidebarStyle: string
|
||||
sidebarStyle: string,
|
||||
currHeadMenuName: any,
|
||||
website: Object
|
||||
}
|
||||
|
||||
const theme = storage.get('theme') ?? {}
|
||||
@ -25,7 +28,8 @@ const useSystemStore = defineStore('system', {
|
||||
sidebar: theme.sidebar ?? 'oneType',
|
||||
lang: storage.get('lang') ?? 'zh-cn',
|
||||
sidebarStyle: theme.sidebarStyle ?? 'threeType',
|
||||
currHeadMenuName: ''
|
||||
currHeadMenuName: '',
|
||||
website: {}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -41,8 +45,13 @@ const useSystemStore = defineStore('system', {
|
||||
this.menuIsCollapse = value
|
||||
storage.set({ key: 'menuiscollapse', data: value })
|
||||
useCssVar('--aside-width').value = value ? 'calc(var(--el-menu-icon-width) + var(--el-menu-base-level-padding) * 2)' : '210px'
|
||||
},
|
||||
async getWebsiteInfo() {
|
||||
await getWebConfig().then(({ data }) => {
|
||||
this.website = data
|
||||
}).catch()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default useSystemStore
|
||||
export default useSystemStore
|
||||
|
||||
@ -5,6 +5,7 @@ import storage from '@/utils/storage'
|
||||
import router from '@/router'
|
||||
import { formatRouters, findFirstValidRoute } from '@/router/routers'
|
||||
import useTabbarStore from './tabbar'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
interface User {
|
||||
token: string,
|
||||
@ -41,6 +42,9 @@ const userStore = defineStore('user', {
|
||||
return new Promise((resolve, reject) => {
|
||||
login(form, app_type)
|
||||
.then((res) => {
|
||||
if (app_type == 'admin' && Test.empty(res.data.userrole)) {
|
||||
storage.setPrefix('site')
|
||||
}
|
||||
this.token = res.data.token
|
||||
this.userInfo = res.data.userinfo
|
||||
this.siteInfo = res.data.site_info || {}
|
||||
|
||||
@ -38,6 +38,10 @@ html {
|
||||
}
|
||||
|
||||
.main-container{
|
||||
// background-color: #fff;
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
min-height: calc(100vh - 84px);
|
||||
overflow: hidden;
|
||||
.full-container {
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
@ -54,11 +58,11 @@ html {
|
||||
height: 48px;
|
||||
|
||||
.fixed-footer {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
height: inherit;
|
||||
background:var(--el-bg-color-overlay);
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: 10px;
|
||||
left: 10px;
|
||||
bottom: 0;
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
@ -257,7 +261,12 @@ html.dark {
|
||||
// background-color: #636363 !important;
|
||||
// opacity: 1 !important;
|
||||
//}
|
||||
|
||||
.el-card{
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
:root input:-webkit-autofill, textarea:-webkit-autofill, select:-webkit-autofill {
|
||||
box-shadow: 0 0 50px 50px white inset;
|
||||
}
|
||||
.text-page-title{
|
||||
line-height: 32px;
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3883393 */
|
||||
src: url('//at.alicdn.com/t/c/font_3883393_au7p2pnufj8.woff2?t=1711684151959') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_au7p2pnufj8.woff?t=1711684151959') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_au7p2pnufj8.ttf?t=1711684151959') format('truetype');
|
||||
src: url('//at.alicdn.com/t/c/font_3883393_eqk4fw84z0e.woff2?t=1712631982793') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_eqk4fw84z0e.woff?t=1712631982793') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_eqk4fw84z0e.ttf?t=1712631982793') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,22 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconxingzhuang-wenzi:before {
|
||||
content: "\eb99";
|
||||
}
|
||||
|
||||
.iconxiaochengxu1:before {
|
||||
content: "\e631";
|
||||
}
|
||||
|
||||
.iconshipin1:before {
|
||||
content: "\e63a";
|
||||
}
|
||||
|
||||
.icontupian:before {
|
||||
content: "\e667";
|
||||
}
|
||||
|
||||
.iconfenxiang:before {
|
||||
content: "\e655";
|
||||
}
|
||||
@ -21,10 +37,6 @@
|
||||
content: "\e63f";
|
||||
}
|
||||
|
||||
.iconhuojian1:before {
|
||||
content: "\e633";
|
||||
}
|
||||
|
||||
.iconkaifazujian:before {
|
||||
content: "\e640";
|
||||
}
|
||||
|
||||
@ -1,10 +1,38 @@
|
||||
{
|
||||
"id": "3883393",
|
||||
"name": "系统",
|
||||
"name": "niushop-vite-admin",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon",
|
||||
"description": "系统图标",
|
||||
"description": "niushop-vite admin端",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "4354254",
|
||||
"name": "形状-文字",
|
||||
"font_class": "xingzhuang-wenzi",
|
||||
"unicode": "eb99",
|
||||
"unicode_decimal": 60313
|
||||
},
|
||||
{
|
||||
"icon_id": "4424124",
|
||||
"name": "小程序",
|
||||
"font_class": "xiaochengxu1",
|
||||
"unicode": "e631",
|
||||
"unicode_decimal": 58929
|
||||
},
|
||||
{
|
||||
"icon_id": "27876594",
|
||||
"name": "视频1",
|
||||
"font_class": "shipin1",
|
||||
"unicode": "e63a",
|
||||
"unicode_decimal": 58938
|
||||
},
|
||||
{
|
||||
"icon_id": "29941073",
|
||||
"name": "图片",
|
||||
"font_class": "tupian",
|
||||
"unicode": "e667",
|
||||
"unicode_decimal": 58983
|
||||
},
|
||||
{
|
||||
"icon_id": "2672195",
|
||||
"name": "分享",
|
||||
|
||||
@ -2,6 +2,7 @@ import axios, { HttpStatusCode } from 'axios'
|
||||
import type { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig } from 'axios'
|
||||
import { getToken, isUrl } from './common';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { MessageParams } from 'element-plus'
|
||||
import { t } from '@/lang'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import storage from '@/utils/storage'
|
||||
@ -55,7 +56,7 @@ class Request {
|
||||
const res = response.data
|
||||
if (res.code != 1) {
|
||||
this.handleAuthError(res.code)
|
||||
if (res.code != 401 && response.config.showErrorMessage !== false) ElMessage({ message: res.msg, type: 'error', dangerouslyUseHTMLString: true, duration: 5000 })
|
||||
if (res.code != 401 && response.config.showErrorMessage !== false) this.showElMessage({ message: res.msg, type: 'error', dangerouslyUseHTMLString: true, duration: 5000 })
|
||||
return Promise.reject(new Error(res.msg || 'Error'))
|
||||
} else {
|
||||
if (response.config.showSuccessMessage) ElMessage({ message: res.msg, type: 'success' })
|
||||
@ -168,7 +169,7 @@ class Request {
|
||||
const baseURL = isUrl(err.config.baseURL) ? err.config.baseURL : `${location.origin}${err.config.baseURL}`
|
||||
errMessage = baseURL + t('axios.baseUrlError')
|
||||
}
|
||||
errMessage && ElMessage({ dangerouslyUseHTMLString: true, duration: 5000, message: errMessage, type: 'error' })
|
||||
errMessage && this.showElMessage({ dangerouslyUseHTMLString: true, duration: 5000, message: errMessage, type: 'error' })
|
||||
}
|
||||
|
||||
private handleAuthError(code: number) {
|
||||
@ -178,6 +179,18 @@ class Request {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private messageCache = new Map();
|
||||
|
||||
private showElMessage(options: MessageParams) {
|
||||
const cacheKey = options.message
|
||||
const cachedMessage = this.messageCache.get(cacheKey);
|
||||
|
||||
if (!cachedMessage || Date.now() - cachedMessage.timestamp > 5000) { // 5秒内重复内容不再弹出,可自定义过期时间
|
||||
this.messageCache.set(cacheKey, { timestamp: Date.now() });
|
||||
ElMessage(options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Request()
|
||||
|
||||
@ -14,6 +14,10 @@ class Storage {
|
||||
this.prefix = getAppType() == 'admin' ? 'admin' : 'site'
|
||||
}
|
||||
|
||||
public setPrefix(prefix: string) {
|
||||
this.prefix = prefix
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存
|
||||
* @param param
|
||||
|
||||
@ -88,8 +88,11 @@ class ExceptionHandle extends Handle
|
||||
'trace' => $e->getTrace(),
|
||||
'previous' => $e->getPrevious(),
|
||||
] : [];
|
||||
// 添加自定义异常处理机制
|
||||
|
||||
// 添加自定义异常处理机制
|
||||
if (strpos($e->getMessage(), 'open_basedir') !== false) {
|
||||
return fail('OPEN_BASEDIR_ERROR');
|
||||
}
|
||||
if ($e instanceof DbException) {
|
||||
return fail(get_lang('DATA_GET_FAIL').':'.$e->getMessage(), [
|
||||
'file' => $e->getFile(),
|
||||
|
||||
@ -43,6 +43,17 @@ class Diy extends BaseAdminController
|
||||
return success(( new DiyService() )->getPage($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取自定义页面分页列表,轮播搜索组件用
|
||||
* @return Response
|
||||
*/
|
||||
public function getPageByCarouselSearch()
|
||||
{
|
||||
$data = $this->request->params([
|
||||
]);
|
||||
return success(( new DiyService() )->getPageByCarouselSearch($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取自定义页面列表
|
||||
* @return Response
|
||||
|
||||
@ -25,6 +25,7 @@ use think\Response;
|
||||
|
||||
class Login extends BaseAdminController
|
||||
{
|
||||
use WapTrait;
|
||||
|
||||
/**
|
||||
* 登录
|
||||
@ -69,6 +70,12 @@ class Login extends BaseAdminController
|
||||
}
|
||||
|
||||
public function test(){
|
||||
|
||||
$this->addon = 'shop';
|
||||
$this->uninstallPageCode(project_path(). 'uni-app/src/');
|
||||
}
|
||||
|
||||
public function geAddonPackagePath(string $addon)
|
||||
{
|
||||
return root_path() . 'addon' .DIRECTORY_SEPARATOR . $addon . DIRECTORY_SEPARATOR . 'package' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user