This commit is contained in:
wangchen147 2024-04-13 18:23:54 +08:00
parent ed7ae681bf
commit 9bc5f0f3bd
535 changed files with 4389 additions and 2664 deletions

View File

@ -1,7 +1,7 @@
NODE_ENV = 'development'
# api请求地址
VITE_APP_BASE_URL='/adminapi/'
VITE_APP_BASE_URL=''
# 图片服务器地址
VITE_IMG_DOMAIN=''

View File

@ -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']

View File

@ -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())

View File

@ -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})
}
/**
*

View File

@ -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 })
}
/**

View File

@ -159,7 +159,7 @@ export function getWebsite() {
}
/**
*
*
* @returns
*/
export function getWebConfig() {

View File

@ -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')
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View 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>

View File

@ -6,6 +6,9 @@
"menuTypeMenu": "菜单",
"menuTypeButton": "按钮",
"menuDeleteTips": "删除菜单会删除当前菜单以及该菜单下所有子菜单,是否确认删除?",
"initializeMenu":"重置菜单",
"initializeMenuTipsOne":"重置菜单会将应用或插件的dict目录下的菜单配置文件中菜单配置更新到数据库一般用做开发者修改了dict菜单配置文件后同步到数据库操作。",
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常不允诛进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
"addMenu": "添加菜单",
"updateMenu": "编辑菜单",
"routePath": "路由路径",

View File

@ -7,6 +7,9 @@
"menuTypeButton": "按钮",
"menuDeleteTips": "确定要删除该菜单吗?",
"addMenu": "添加菜单",
"initializeMenu":"重置菜单",
"initializeMenuTipsOne":"重置菜单会将应用或插件的dict目录下的菜单配置文件中菜单配置更新到数据库一般用做开发者修改了dict菜单配置文件后同步到数据库操作。",
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常不允诛进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
"updateMenu": "编辑菜单",
"routePath": "路由路径",
"viewPath": "组件路径",

View File

@ -20,4 +20,4 @@
"subscribeMessage": "订阅消息",
"weappRelease": "版本管理",
"alert":"您正在体验通用版小程序,不发布你将不可用"
}
}

View File

@ -15,5 +15,6 @@
"customMenu": "自定义菜单",
"wechatAccessBtn": "查看审核进度",
"clickAccess2":"扫描二维码进入微信公众号",
"wechatTemplate": "模板消息"
}
"wechatTemplate": "模板消息",
"reply": "自动回复"
}

View File

@ -0,0 +1,15 @@
{
"ruleName": "规则名称",
"ruleNameTips": "规则名不能为空且最多60个字",
"ruleNamePlaceholder": "请输入规则名称",
"keywordPlaceholder": "请输入关键字",
"contentPlaceholder": "请选择回复内容",
"allMatching": "全匹配",
"fuzzyMatching": "模糊匹配",
"keyword": "关键字",
"content": "回复内容",
"replyMethod": "回复方式",
"replyMethodAll": "全部回复",
"replyMethodRand": "随机回复一条",
"addReplyContent": "添加回复内容"
}

View File

@ -19,5 +19,6 @@
"wechatAccessFlow": "接入流程",
"customMenu": "自定义菜单",
"wechatTemplate": "模板消息",
"menusEmptyTips": "空菜单,不能保存与发布。"
}
"menusEmptyTips": "空菜单,不能保存与发布。",
"reply": "自动回复"
}

View File

@ -0,0 +1,12 @@
{
"title": "自动回复",
"wechatAccessFlow": "接入流程",
"wechatSet": "公众号配置",
"customMenu": "自定义菜单",
"wechatTemplate": "模板消息",
"reply": "自动回复",
"keywordReply": "关键词回复",
"defaultReply": "默认回复",
"subscribeReply": "关注回复",
"replyDeleteTips": "确定要删除该条回复吗?"
}

View File

@ -21,5 +21,5 @@
"wechatAccessFlow": "接入流程",
"customMenu": "自定义菜单",
"wechatTemplate": "模板消息",
"id": "id"
}
"reply": "自动回复"
}

View File

@ -1,5 +1,5 @@
{
"copyrightEdit":"版权设置",
"copyrightEdit":"基础设置",
"putOnRecordEdit":"备案设置",
"companyName": "公司名称",

View File

@ -1,6 +1,6 @@
{
"buyerNotice": "会员消息",
"sellerNotice":"平台消息",
"sellerNotice":"商家消息",
"sms":"短信",
"weapp":"微信小程序",
"wechat":"微信公众号",
@ -18,5 +18,6 @@
"weappTempKey" : "模板编号",
"smsId":"短信模版ID",
"smsIdPlaceholder":"短信模版ID",
"noticeType":"消息类型"
}
"noticeType":"消息类型",
"addon": "所属应用"
}

View File

@ -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
})
}
})
}

View File

@ -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>

View File

@ -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>

View File

@ -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')">

View File

@ -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>

View File

@ -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,

View File

@ -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(()=>{})
}
/**
* 添加菜单

View File

@ -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

View File

@ -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(()=>{})
}
/**
* 添加菜单
*/

View File

@ -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,

View File

@ -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]">

View File

@ -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>

View File

@ -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> -->

View File

@ -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">

View File

@ -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">

View File

@ -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]">

View File

@ -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,

View File

@ -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>

View File

@ -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 /

View File

@ -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>

View File

@ -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>

View 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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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]);

View File

@ -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>

View File

@ -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>

View File

@ -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();
})
/**
* 获取站点账单记录列表
*/

View File

@ -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 = () => {

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -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">

View File

@ -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
})

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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)

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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')

View File

@ -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>

View File

@ -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>

View File

@ -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 {

View File

@ -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>

View File

@ -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>

View File

@ -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": "关闭左侧",

View File

@ -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>

View File

@ -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)

View File

@ -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>

View File

@ -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>();

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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()

View File

@ -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')
}
]
}
]
}

View File

@ -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){

View File

@ -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

View File

@ -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 || {}

View File

@ -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;
}

View File

@ -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";
}

View File

@ -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": "分享",

View File

@ -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()

View File

@ -14,6 +14,10 @@ class Storage {
this.prefix = getAppType() == 'admin' ? 'admin' : 'site'
}
public setPrefix(prefix: string) {
this.prefix = prefix
}
/**
*
* @param param

View File

@ -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(),

View File

@ -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

View File

@ -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