mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2026-01-24 19:48:10 +00:00
0.5.2
This commit is contained in:
parent
ecc7c620c1
commit
3dd2b3e0a8
@ -55,4 +55,4 @@
|
||||
"vite": "4.1.0",
|
||||
"vue-tsc": "1.0.24"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,6 +184,15 @@ export function unlockUser(uid: number) {
|
||||
return request.put(`site/user/unlock/${uid}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*
|
||||
* @param uid
|
||||
*/
|
||||
export function deleteUser(uid: number) {
|
||||
return request.delete(`site/user/${uid}`)
|
||||
}
|
||||
|
||||
/***************************************************** 操作日志 **************************************************/
|
||||
|
||||
/**
|
||||
|
||||
@ -39,6 +39,14 @@ export function deleteUser(uid: number) {
|
||||
return request.delete(`user/user/${uid}`, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
* @param uid
|
||||
*/
|
||||
export function editUser(params: Record<string, any>) {
|
||||
return request.put(`user/user/${params.uid}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有用户列表
|
||||
* @param params
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<div class="mt-[20px]" v-for="(item, index) in upgradeContent.version_list" :key="index">
|
||||
<div class="font-bold text-lg">{{ item.version_no }}</div>
|
||||
<div class="mt-[5px]" v-if="item.release_time">{{ item.release_time }}</div>
|
||||
<div class="mt-[10px] p-[10px] rounded bg-[#f4f4f5] whitespace-pre" v-if="item.upgrade_log" v-html="item.upgrade_log"></div>
|
||||
<div class="mt-[10px] p-[10px] rounded bg-[#f4f4f5] whitespace-pre-wrap !break-all" v-if="item.upgrade_log" v-html="item.upgrade_log"></div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
@ -28,5 +28,6 @@
|
||||
"manager": "用户",
|
||||
"managerPlaceholder": "请选择用户",
|
||||
"managerTips": "选择或者新增用户作为管理员",
|
||||
"newAddManager": "新增用户"
|
||||
}
|
||||
"newAddManager": "新增用户",
|
||||
"userDeleteTips": "是否要删除该管理员?"
|
||||
}
|
||||
|
||||
@ -19,5 +19,6 @@
|
||||
"dataValuePlaceholder":"请输入数据值",
|
||||
"sortPlaceholder":"数值越大越排前",
|
||||
"momePlaceholder":"请输入备注",
|
||||
"createTime":"创建时间"
|
||||
}
|
||||
"createTime":"创建时间",
|
||||
"keyFormatTips": "关键字只允许输入字母和下划线"
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
{
|
||||
"title": "插件名称",
|
||||
"key": "插件标识"
|
||||
"key": "插件标识",
|
||||
"type": "插件类型",
|
||||
"app": "应用",
|
||||
"addon": "插件"
|
||||
}
|
||||
@ -1,48 +1,50 @@
|
||||
{
|
||||
"orderNo":"订单编号",
|
||||
"orderStatus":"订单状态",
|
||||
"orderNoPlaceholder":"请输入订单编号",
|
||||
"createTime":"创建时间",
|
||||
"rechargeMoney":"充值金额",
|
||||
"totalTransfered":"累计提现(元)",
|
||||
"totalCashOuting":"提现中(元)",
|
||||
"transfered":"累计提现",
|
||||
"cashOuting":"提现中",
|
||||
"orderMoney":"订单金额",
|
||||
"member":"买家",
|
||||
"orderFromName":"订单来源",
|
||||
"payTypeName":"支付方式",
|
||||
"startDate":"开始时间",
|
||||
"endDate":"结束时间",
|
||||
"namePlaceholder":"请选择",
|
||||
"applyTime":"申请时间",
|
||||
"cashOutStatus":"提现状态",
|
||||
"actualTransferAmount":"实际转账金额",
|
||||
"cashOutCommission":"提现手续费",
|
||||
"applicationForWithdrawalAmount":"申请提现金额",
|
||||
"cashOutMethod":"提现方式",
|
||||
"cashOutAccountType":"会员账户",
|
||||
"memberInfo":"会员信息",
|
||||
"toBeReviewed":"待审核",
|
||||
"toBeTransferred":"待转账",
|
||||
"transferred":"已转账",
|
||||
"turnDown":"拒绝",
|
||||
"transfer": "转账",
|
||||
"detail": "详情",
|
||||
"auditFailure": "审核失败",
|
||||
"successfulAudit": "审核成功",
|
||||
"rejectionAudit": "拒绝审核",
|
||||
"reasonsRefusal": "拒绝理由",
|
||||
"reasonsRefusalPlaceholder": "请输入拒绝理由",
|
||||
"isTransfer": "是否确认转账",
|
||||
"nickname":"会员名称",
|
||||
"headimg":"会员头像",
|
||||
"cashOutDetail":"提现详情",
|
||||
"cashOutMoney": "转账金额",
|
||||
"orderNo": "订单编号",
|
||||
"orderStatus": "订单状态",
|
||||
"orderNoPlaceholder": "请输入订单编号",
|
||||
"createTime": "创建时间",
|
||||
"rechargeMoney": "充值金额",
|
||||
"totalTransfered": "累计提现(元)",
|
||||
"totalCashOuting": "提现中(元)",
|
||||
"transfered": "累计提现",
|
||||
"cashOuting": "提现中",
|
||||
"orderMoney": "订单金额",
|
||||
"member": "买家",
|
||||
"orderFromName": "订单来源",
|
||||
"payTypeName": "支付方式",
|
||||
"startDate": "开始时间",
|
||||
"endDate": "结束时间",
|
||||
"namePlaceholder": "请选择",
|
||||
"applyTime": "申请时间",
|
||||
"cashOutStatus": "提现状态",
|
||||
"actualTransferAmount": "实际转账金额",
|
||||
"cashOutCommission": "提现手续费",
|
||||
"applicationForWithdrawalAmount": "申请提现金额",
|
||||
"cashOutMethod": "提现方式",
|
||||
"cashOutAccountType": "会员账户",
|
||||
"memberInfo": "会员信息",
|
||||
"toBeReviewed": "待审核",
|
||||
"toBeTransferred": "待转账",
|
||||
"transferred": "已转账",
|
||||
"turnDown": "拒绝",
|
||||
"transfer": "转账",
|
||||
"detail": "详情",
|
||||
"auditFailure": "审核失败",
|
||||
"successfulAudit": "审核成功",
|
||||
"rejectionAudit": "拒绝审核",
|
||||
"reasonsRefusal": "拒绝理由",
|
||||
"reasonsRefusalPlaceholder": "请输入拒绝理由",
|
||||
"isTransfer": "是否确认转账",
|
||||
"nickname": "会员名称",
|
||||
"headimg": "会员头像",
|
||||
"cashOutDetail": "提现详情",
|
||||
"cashOutMoney": "转账金额",
|
||||
"auditTime": "审核时间",
|
||||
"transferTime": "转账时间",
|
||||
"memberInfoPlaceholder":"请输入会员名称/会员昵称/手机号",
|
||||
"memberInfoPlaceholder": "请输入会员名称/会员昵称/手机号",
|
||||
"cashOutNumber": "提现单号",
|
||||
"cashOutNumberPlaceholder": "请输入提现单号"
|
||||
|
||||
"cashOutNumberPlaceholder": "请输入提现单号",
|
||||
"alipayAccount": "支付宝账号",
|
||||
"bankName": "银行名称",
|
||||
"bankAccount": "银行卡号"
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
"calendarSign": "日历签到",
|
||||
"periodSign": "周期签到",
|
||||
"daySignAward": "日签奖励",
|
||||
"daySignAwardPlaceholder": "请选择日签奖励",
|
||||
"continueSignAward": "连签奖励",
|
||||
"calendarSignTip": "用户根据日期进行打卡,连续签到一定天数可即可获得连签奖励。",
|
||||
"periodSignTip": "用户在规定的周期内完成签到可以获得奖励;一个周期结束后将进入下一个循环周期。",
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"businessHours":"营业时间",
|
||||
"createTime":"创建时间",
|
||||
"expireTime":"到期时间",
|
||||
"siteNamePlaceholder":"请输入站点名称",
|
||||
"siteNamePlaceholder":"请输入站点名称/编号",
|
||||
"createTimePlaceholder":"请输入创建时间",
|
||||
"addSite":"添加站点",
|
||||
"editSite":"编辑站点",
|
||||
@ -52,5 +52,6 @@
|
||||
"siteDomainTipsTwo": "需要将域名配置到您的服务器,同时域名需要解析您的服务器才可生效",
|
||||
"siteDomainTipsThree": "站点域名不需要加http或者https,末尾不需要加/",
|
||||
"toSite": "访问站点",
|
||||
"noPermission": "您没有该站点的管理权限"
|
||||
"noPermission": "您没有该站点的管理权限",
|
||||
"closeSiteTips": "是否要停止该站点?"
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"startDate": "开始时间",
|
||||
"loginTime": "最后登录时间",
|
||||
"addUser": "添加用户",
|
||||
"updateUser": "编辑用户",
|
||||
"username": "账号",
|
||||
"passwordPlaceholder": "请输入用户密码",
|
||||
"usernamePlaceholder": "请输入用户账号",
|
||||
|
||||
@ -29,5 +29,5 @@
|
||||
"addCron": "添加任务",
|
||||
"cronTimeTips": "任务周期时间不能为空",
|
||||
"cronTipsOne": "启动计划任务方式:",
|
||||
"cronTipsTwo": "1、使用命令启动:php think cron:schedule 如果更改了任务周期、状态、删除任务等操作后,需要重新启动下 php think cron:schedule 确保生效"
|
||||
}
|
||||
"cronTipsTwo": "1、使用命令启动:php think workerman 如果更改了任务周期、状态、删除任务等操作后,需要重新启动下 php think workerman 确保生效"
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
<div class="text-page-title mb-[20px]">历史版本</div>
|
||||
<el-timeline>
|
||||
<el-timeline-item :timestamp="item['release_time'] + ' 版本:' + item['version_no']" v-for="(item,index) in frameworkVersionList" type="primary" :hollow="true" placement="top" :key="index">
|
||||
<div class="mt-[10px] p-[20px] bg-overlay rounded-md timeline-log-wrap whitespace-pre" v-if="item['upgrade_log']">
|
||||
<div class="mt-[10px] p-[20px] bg-overlay rounded-md timeline-log-wrap whitespace-pre-wrap" v-if="item['upgrade_log']">
|
||||
<div v-html="item['upgrade_log']"></div>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<el-image class="w-[40px] h-[40px] mr-[10px]" :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-[40px] h-[40px]" src="@/app/assets/images/index/app_default.png" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
|
||||
@ -68,6 +68,7 @@
|
||||
<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="deleteEvent(row.uid)">{{ t('delete') }}</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-button link disabled>{{ t('adminDisabled') }}</el-button>
|
||||
@ -91,7 +92,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getUserList, lockUser, unlockUser } from '@/app/api/site'
|
||||
import { getUserList, lockUser, unlockUser, deleteUser } from '@/app/api/site'
|
||||
import EditUser from '@/app/views/auth/components/edit-user.vue'
|
||||
import { img } from '@/utils/common'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
@ -198,6 +199,21 @@ const unlockEvent = (id: number) => {
|
||||
})
|
||||
}
|
||||
|
||||
const deleteEvent = (uid: number) => {
|
||||
ElMessageBox.confirm(t('userDeleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
deleteUser(uid).then(() => {
|
||||
loadUserList()
|
||||
}).catch(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@ -75,7 +75,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="failReasonDialogVisible" :title="t('failReason')" width="60%">
|
||||
<el-scrollbar class="h-[60vh] w-full whitespace-pre p-[20px]">
|
||||
<el-scrollbar class="h-[60vh] w-full whitespace-pre-wrap p-[20px]">
|
||||
<div v-html="failReason"></div>
|
||||
</el-scrollbar>
|
||||
</el-dialog>
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('key')" prop="key">
|
||||
<el-input v-model.trim="formData.key" clearable maxlength="40" show-word-limit :placeholder="t('keyPlaceholder')" class="input-width" />
|
||||
<p class="form-tip">{{ t('keyFormatTips') }}</p>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('memo')">
|
||||
<el-input v-model="formData.memo" type="textarea" clearable :placeholder="t('memoPlaceholder')" class="input-width" />
|
||||
@ -50,7 +51,17 @@ const formRules = computed(() => {
|
||||
{ required: true, message: t('namePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
key: [
|
||||
{ required: true, message: t('keyPlaceholder'), trigger: 'blur' }
|
||||
{ required: true, message: t('keyPlaceholder'), trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (/^[a-zA-Z_]+$/.test(value)) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error(t('keyFormatTips')))
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
data: [
|
||||
{ required: true, message: t('dataPlaceholder'), trigger: 'blur' }
|
||||
|
||||
@ -87,15 +87,15 @@
|
||||
<el-input v-model.trim="item.title.text" :placeholder="t('activeCubeTitlePlaceholder')" clearable maxlength="4" show-word-limit/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('activeCubeSubTitleTextColor')" v-show="selectBlockStyle.value == 'style-3'">
|
||||
<el-form-item :label="t('activeCubeSubTitleTextColor')" v-show="diyStore.editComponent.blockStyle.value == 'style-3'">
|
||||
<el-color-picker v-model="item.title.textColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('activeCubeSubTitle')" v-if="selectBlockStyle.value != 'style-3'">
|
||||
<el-input v-model.trim="item.subTitle.text" :placeholder="t('activeCubeSubTitlePlaceholder')" clearable :maxlength="(selectBlockStyle.value != 'style-4' ? '6' : '4')" show-word-limit/>
|
||||
<el-form-item :label="t('activeCubeSubTitle')" v-if="diyStore.editComponent.blockStyle.value != 'style-3'">
|
||||
<el-input v-model.trim="item.subTitle.text" :placeholder="t('activeCubeSubTitlePlaceholder')" clearable :maxlength="(diyStore.editComponent.blockStyle.value != 'style-4' ? '6' : '4')" show-word-limit/>
|
||||
</el-form-item>
|
||||
|
||||
<div v-show="selectBlockStyle.value == 'style-4'">
|
||||
<div v-show="diyStore.editComponent.blockStyle.value == 'style-4'">
|
||||
<el-form-item :label="t('activeCubeSubTitleTextColor')">
|
||||
<el-color-picker v-model="item.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
@ -111,7 +111,7 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div v-show="selectBlockStyle.value != 'style-4' && selectBlockStyle.value != 'style-3'">
|
||||
<div v-show="diyStore.editComponent.blockStyle.value != 'style-4' && diyStore.editComponent.blockStyle.value != 'style-3'">
|
||||
<el-form-item :label="t('activeCubeButton')">
|
||||
<el-input v-model.trim="item.moreTitle.text" :placeholder="t('activeCubeButtonPlaceholder')" clearable maxlength="3" show-word-limit/>
|
||||
</el-form-item>
|
||||
|
||||
@ -70,14 +70,14 @@
|
||||
</el-card>
|
||||
|
||||
<!--添加页面-->
|
||||
<el-dialog v-model="dialogVisible" :title="t('addPageTips')" width="25%">
|
||||
<el-dialog v-model="dialogVisible" :title="t('addPageTips')" width="350px">
|
||||
|
||||
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules">
|
||||
<el-form-item :label="t('title')" prop="title">
|
||||
<el-input v-model="formData.title" :placeholder="t('titlePlaceholder')" clearable maxlength="12" show-word-limit class="w-full" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('typeName')" prop="type">
|
||||
<el-select v-model="formData.type" :placeholder="t('pageTypePlaceholder')" class="w-full">
|
||||
<el-select v-model="formData.type" :placeholder="t('pageTypePlaceholder')" class="!w-full">
|
||||
<el-option v-for="(item, key) in pageType" :label="item.title" :value="key" :key="key"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@ -17,7 +17,13 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="key" :label="t('key')" min-width="80"/>
|
||||
<el-table-column prop="key" :label="t('key')" min-width="120"/>
|
||||
|
||||
<el-table-column :label="t('type')" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.info.type === 'app' ? t('app') : t('addon') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="160">
|
||||
<template #default="{ row }">
|
||||
|
||||
@ -129,7 +129,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="230">
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button v-for="(item, index) in operationBtn[row.status.toString()].value" :key="index + 'a'"
|
||||
@click="fnProcessing(operationBtn[row.status.toString()].clickArr[index], row)"
|
||||
@ -158,6 +158,19 @@
|
||||
<el-form-item :label="t('cashOutMethod')">
|
||||
<div class="input-width"> {{ Transfertype[cashOutInfo.transfer_type].name }} </div>
|
||||
</el-form-item>
|
||||
<template v-if="cashOutInfo.transfer_type == 'alipay'">
|
||||
<el-form-item :label="t('alipayAccount')">
|
||||
<div class="input-width"> {{ cashOutInfo.transfer_account }} </div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-if="cashOutInfo.transfer_type == 'bank'">
|
||||
<el-form-item :label="t('bankName')">
|
||||
<div class="input-width"> {{ cashOutInfo.transfer_bank }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('bankAccount')">
|
||||
<div class="input-width"> {{ cashOutInfo.transfer_account }} </div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item :label="t('applicationForWithdrawalAmount')">
|
||||
<div class="input-width"> {{ cashOutInfo.apply_money }} </div>
|
||||
</el-form-item>
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
<span :class="['px-[10px] cursor-pointer h-[35px] leading-[35px] inline-block', {'text-[var(--el-color-primary)]': params.app == item.key}]" @click="cutAppFn(item.key)" v-for="(item,index) in addonList" :key="index">{{item.title}}</span>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<el-input v-model="params.keywords" class="!w-[300px] !h-[34px]" placeholder="请输入要搜索的站点名称" @keyup.enter.native="getHomeSiteFn()">
|
||||
<el-input v-model="params.keywords" class="!w-[300px] !h-[34px]" placeholder="请输入要搜索的站点名称/编号" @keyup.enter.native="getHomeSiteFn()">
|
||||
<template #suffix>
|
||||
<el-icon @click.stop="getHomeSiteFn()" class="cursor-pointer">
|
||||
<Search />
|
||||
@ -95,7 +95,7 @@
|
||||
:class="{'bg-[#F6F7FF] border-[#466CEA]': createSiteData.formData.group_id == item.group_id ,'ml-[20px]': index > 0, ' ml-[10px]': index == 0, 'mr-[10px]': (siteGroup.length-1) == index }"
|
||||
@click="createSiteData.formData.group_id = item.group_id"
|
||||
>
|
||||
<div class="w-[140px] h-[40px] truncate text-white text-[16px] text-center leading-[40px] creatBg relative -left-[1px] -top-[2px]">
|
||||
<div class="w-[140px] h-[40px] px-[15px] truncate text-white text-[16px] text-center leading-[40px] creatBg relative -left-[1px] -top-[2px]">
|
||||
{{ item.site_group.group_name }}
|
||||
</div>
|
||||
<el-scrollbar class="flex pb-[20px] pt-[4px] box-border !h-[260px]">
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
{{ t('buyLabel') }}
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" round @click="handleCloudBuild" :loading="cloudBuildRef?.loading">{{ t('cloudBuild') }}</el-button>
|
||||
<el-button type="primary" round @click="handleCloudBuild" :loading="cloudBuildRef?.loading" :disabled="authLoading">{{ t('cloudBuild') }}</el-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@ -42,12 +42,15 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="flex flex-col justify-center pl-[20px] font-500 text-[13px]">
|
||||
<div class="flex-1 w-0 flex flex-col justify-center pl-[20px] font-500 text-[13px]">
|
||||
<div class="w-[236px] truncate leading-[18px]">{{ row.title }}</div>
|
||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-if="row.install_info && Object.keys(row.install_info)?.length">{{ row.install_info.version }}</div>
|
||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-else>{{ row.version }}</div>
|
||||
<div class="mt-[3px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">
|
||||
<el-tag type="danger" size="small">{{ t('newVersion') }}{{ row.version }}</el-tag>
|
||||
<div class="mt-[3px] flex flex-nowrap">
|
||||
<el-tag type="danger" size="small" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">{{ t('newVersion') }}{{ row.version }}</el-tag>
|
||||
<el-tooltip v-if="versionJudge(row)" effect="dark" content="该插件与框架版本不兼容,可能存在未知问题" placement="top-start" >
|
||||
<el-tag type="info" size="small" class="ml-[3px]">该插件与框架版本不兼容,可能存在未知问题</el-tag>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -376,6 +379,7 @@ import { t } from '@/lang'
|
||||
import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, cloudInstallAddon, getAddonInstalltask, getAddonCloudInstallLog, preUninstallCheck, cancelInstall } from '@/app/api/addon'
|
||||
import { deleteAddonDevelop } from '@/app/api/tools'
|
||||
import { downloadVersion, getAuthInfo, setAuthInfo } from '@/app/api/module'
|
||||
import { getVersions } from '@/app/api/auth'
|
||||
import { ElMessage, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus'
|
||||
import 'vue-web-terminal/lib/theme/dark.css'
|
||||
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
||||
@ -397,6 +401,11 @@ const installAfterTips = ref<string[]>([])
|
||||
const userStore = useUserStore()
|
||||
const unloadHintDialog = ref(false)
|
||||
const terminalRef = ref(null)
|
||||
const frameworkVersion = ref('')
|
||||
|
||||
getVersions().then(res => {
|
||||
frameworkVersion.value = res.data.version.version
|
||||
})
|
||||
|
||||
const currDownData = ref()
|
||||
const downEventHintFn = () => {
|
||||
@ -902,6 +911,14 @@ const deleteAddonFn = (key: string) => {
|
||||
})
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
const versionJudge = (row: any) => {
|
||||
if (!row.support_version) return true
|
||||
const supportVersionApp = row.support_version.split('.')
|
||||
const frameworkVersionArr = frameworkVersion.value.split('.')
|
||||
if (parseFloat(`${supportVersionApp[0]}.${supportVersionApp[1]}`) < parseFloat(`${frameworkVersionArr[0]}.${frameworkVersionArr[1]}`)) return true
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<el-form :model="formData" :rules="formRules" class="page-form" ref="formRef">
|
||||
<el-form-item :label="t('continueSign')" prop="continue_sign">
|
||||
<el-input class="input-width" v-model.trim="formData.continue_sign" clearable /><span class="ml-[10px]">{{ t('day') }}</span>
|
||||
<el-input class="input-width" v-model.trim="formData.continue_sign" :maxlength="5" clearable /><span class="ml-[10px]">{{ t('day') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('continueSign')" >
|
||||
<div>
|
||||
<div class="flex-1">
|
||||
<div v-for="(item,index) in gifts" :key="index" class="mb-[15px]">
|
||||
<component :is="item.component" v-model="formData[item.key]" ref="giftRefs" v-if="item.component" />
|
||||
</div>
|
||||
@ -15,7 +15,7 @@
|
||||
<el-radio class="mb-[15px]" v-model="formData.receive_limit" :label="1" @change="radioChange($event, 1)">{{ t('noLimit') }}</el-radio>
|
||||
<div class="flex">
|
||||
<el-radio class="!mr-[15px]" v-model="formData.receive_limit" :label="2" @change="radioChange($event, 2)">{{ t('everyOneLimit') }}</el-radio>
|
||||
<el-input class="input-width" v-model="formData.receive_num" clearable /><span class="ml-[10px]">{{ t('time') }}</span>
|
||||
<el-input class="input-width" v-model="formData.receive_num" :maxlength="5" clearable /><span class="ml-[10px]">{{ t('time') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@ -76,18 +76,26 @@ getGiftDict().then(({ data }) => {
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
// 正则表达式
|
||||
const regExp = {
|
||||
required: /[\S]+/,
|
||||
number: /^\d{0,10}$/,
|
||||
digit: /^\d{0,10}(.?\d{0,2})$/,
|
||||
special: /^\d{0,10}(.?\d{0,3})$/
|
||||
}
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
continue_sign: [
|
||||
{ required: true, message: t('continueSignPlaceholder'), trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
if (!Test.digits(formData.value.continue_sign)) {
|
||||
callback('连续签到格式错误')
|
||||
} else if (formData.value.continue_sign <= 0) {
|
||||
callback('连续签到不能小于等于0')
|
||||
} else {
|
||||
callback()
|
||||
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (isNaN(value) || !regExp.number.test(value)) {
|
||||
callback('连续签到天数格式错误')
|
||||
} else if (value <=0) {
|
||||
callback('连续签到天数不能小于等于0')
|
||||
} else{
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
@ -101,7 +109,7 @@ const formRules = reactive<FormRules>({
|
||||
if (Test.empty(formData.value.receive_num)) {
|
||||
callback('请输入限领次数')
|
||||
}
|
||||
if (!Test.digits(formData.value.receive_num)) {
|
||||
if (isNaN(formData.value.receive_num) || !regExp.number.test(formData.value.receive_num)) {
|
||||
callback('限领次数格式错误')
|
||||
}
|
||||
if (formData.value.receive_num <= 0) {
|
||||
|
||||
@ -11,10 +11,10 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('signPeriod')" v-if="formData.is_use">
|
||||
<el-input-number v-model="formData.sign_period" clearable class="input-width" controls-position="right" /><span class="ml-[10px]">天</span>
|
||||
<el-input-number v-model="formData.sign_period" :min="0" :precision="0" clearable class="input-width" controls-position="right" /><span class="ml-[10px]">天</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('daySignAward')" prop="formData.day_award" v-if="formData.is_use">
|
||||
<el-form-item :label="t('daySignAward')" prop="day_award" v-if="formData.is_use">
|
||||
<div v-for="(item, index) in daySignAwardText" :key="index">
|
||||
<span v-if="item.is_use == '1'">{{ item.content }} </span>
|
||||
</div>
|
||||
@ -27,7 +27,7 @@
|
||||
<div class="form-tip">{{ t('daySignAwardTip') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('continueSignAward')" prop="formData.continue_award" v-if="formData.is_use">
|
||||
<el-form-item :label="t('continueSignAward')" prop="continue_award" v-if="formData.is_use">
|
||||
<div>
|
||||
<div class="form-tip">{{ t('continueSignAwardTipTop') }}</div>
|
||||
<div class="mt-[10px]">
|
||||
@ -68,9 +68,9 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('ruleExplain')" prop="formData.rule_explain" v-if="formData.is_use">
|
||||
<el-form-item :label="t('ruleExplain')" prop="rule_explain" v-if="formData.is_use">
|
||||
<div class="flex">
|
||||
<el-input v-model="formData.rule_explain" :placeholder="t('ruleExplainTip')" type="textarea" rows="5" class="textarea-width" clearable />
|
||||
<el-input v-model="formData.rule_explain" :placeholder="t('ruleExplainTip')" type="textarea" maxlength="500" show-word-limit rows="5" class="textarea-width" clearable />
|
||||
<el-button class="ml-[20px]" type="primary" @click="defaultExplainEvent()" plain>{{ t('useDefaultExplain') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@ -89,7 +89,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 连签奖励 -->
|
||||
<el-dialog v-model="continueSignDialog" :title="t('continueSignTitle')" width="1200px" :destroy-on-close="true" v-if="formData.is_use">
|
||||
<el-dialog v-model="continueSignDialog" :title="t('continueSignTitle')" width="800px" :destroy-on-close="true" v-if="formData.is_use">
|
||||
<sign-continue ref="continueRef" v-model="continue_award" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@ -134,7 +134,10 @@ let editIndex = 0 // 连签奖励修改下标
|
||||
const formRules = reactive<FormRules>({
|
||||
sign_period: [
|
||||
{ required: true, message: t('signPeriodTip'), trigger: 'blur' }
|
||||
]
|
||||
],
|
||||
day_award: [
|
||||
{ required: true, message: t('daySignAwardPlaceholder'), trigger: 'change' }
|
||||
],
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<el-checkbox v-model="formData.is_use" :true-label="1" :false-label="0" label="" size="large" />
|
||||
<span class="ml-[10px] el-form-item__label">送</span>
|
||||
<div class="w-[70px]">
|
||||
<el-input v-model.trim="formData.money" clearable />
|
||||
<el-input v-model.trim="formData.money" :maxlength="5" clearable />
|
||||
</div>
|
||||
<span class="ml-[15px] el-form-item__label">元红包</span>
|
||||
</div>
|
||||
@ -33,26 +33,31 @@ const formData = ref({
|
||||
money: ''
|
||||
})
|
||||
const formRef = ref(null)
|
||||
|
||||
// 正则表达式
|
||||
const regExp = {
|
||||
required: /[\S]+/,
|
||||
number: /^\d{0,10}$/,
|
||||
digit: /^\d{0,10}(.?\d{0,2})$/,
|
||||
special: /^\d{0,10}(.?\d{0,3})$/
|
||||
}
|
||||
const formRules = reactive<FormRules>({
|
||||
money: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (formData.value.is_use) {
|
||||
if (Test.empty(formData.value.money)) {
|
||||
if (Test.empty(value)) {
|
||||
callback('请输入红包金额')
|
||||
}
|
||||
if (!Test.amount(formData.value.money)) {
|
||||
}else if (isNaN(value) || !regExp.digit.test(value)) {
|
||||
callback('红包金额格式错误')
|
||||
}
|
||||
if (formData.value.money <= 0) {
|
||||
}else if (value <= 0) {
|
||||
callback('红包金额不能小于等于0')
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@ -32,26 +32,32 @@ const formData = ref({
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
// 正则表达式
|
||||
const regExp = {
|
||||
required: /[\S]+/,
|
||||
number: /^\d{0,10}$/,
|
||||
digit: /^\d{0,10}(.?\d{0,2})$/,
|
||||
special: /^\d{0,10}(.?\d{0,3})$/
|
||||
}
|
||||
const formRules = reactive<FormRules>({
|
||||
num: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (formData.value.is_use) {
|
||||
if (Test.empty(formData.value.num)) {
|
||||
callback('请输入发放积分数量')
|
||||
}
|
||||
if (!Test.digits(formData.value.num)) {
|
||||
if (value.length == 0) {
|
||||
callback('请输入积分数量')
|
||||
} else if (isNaN(value) || !regExp.number.test(value)) {
|
||||
callback('积分数量格式错误')
|
||||
}
|
||||
if (formData.value.num <= 0) {
|
||||
} else if (value <=0) {
|
||||
callback('积分数量不能小于等于0')
|
||||
} else{
|
||||
callback();
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@ -66,14 +66,14 @@
|
||||
</el-card>
|
||||
|
||||
<!--添加海报-->
|
||||
<el-dialog v-model="dialogVisible" :title="t('addPosterTitle')" width="25%">
|
||||
<el-dialog v-model="dialogVisible" :title="t('addPosterTitle')" width="350px">
|
||||
|
||||
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules">
|
||||
<el-form-item :label="t('posterName')" prop="name">
|
||||
<el-input v-model="formData.name" :placeholder="t('posterNamePlaceholder')" clearable maxlength="12" show-word-limit class="w-full" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('posterType')" prop="type">
|
||||
<el-select v-model="formData.type" :placeholder="t('posterTypePlaceholder')" class="w-full">
|
||||
<el-select v-model="formData.type" :placeholder="t('posterTypePlaceholder')" class="!w-full">
|
||||
<el-option v-for="item in posterType" :label="item.name" :value="item.type" :key="item.type"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@ -116,4 +116,8 @@ const back = () => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss">
|
||||
.edui-default .edui-editor{
|
||||
z-index: 1 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,21 +1,29 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="t('addUser')" width="750px" :destroy-on-close="true">
|
||||
<el-dialog v-model="showDialog" :title="formData.uid ? t('updateUser') : t('addUser')" width="750px" :destroy-on-close="true">
|
||||
<el-scrollbar>
|
||||
<div class="max-h-[60vh]">
|
||||
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" autocomplete="off" v-loading="loading">
|
||||
<el-form-item :label="t('username')" prop="username">
|
||||
<el-input v-model="formData.username" clearable :placeholder="t('usernamePlaceholder')" class="input-width" :readonly="real_name_input" @click="real_name_input = false" @blur="real_name_input = true" />
|
||||
<el-input v-model="formData.username" clearable :placeholder="t('usernamePlaceholder')" class="input-width" :readonly="formData.uid" :disabled="formData.uid" @click="realnameInput = false" @blur="realnameInput = true" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('headImg')">
|
||||
<upload-image v-model="formData.head_img" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('userRealName')" prop="real_name">
|
||||
<el-input v-model.trim="formData.real_name" :placeholder="t('userRealNamePlaceholder')" :readonly="realnameInput" @click="realnameInput = false" @blur="realnameInput = true" clearable class="input-width" maxlength="10" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('password')" prop="password">
|
||||
<el-input v-model="formData.password" clearable :placeholder="t('passwordPlaceholder')" class="input-width" :show-password="true" type="password" :readonly="password_input" @click="password_input = false" @blur="password_input = true" />
|
||||
<el-input v-model="formData.password" clearable :placeholder="t('passwordPlaceholder')" class="input-width" :show-password="true" type="password" :readonly="passwordInput" @click="passwordInput = false" @blur="passwordInput = true" />
|
||||
</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" :readonly="confirm_password_input" @click="confirm_password_input = false" @blur="confirm_password_input = true" />
|
||||
<el-input v-model="formData.confirm_password" :placeholder="t('confirmPasswordPlaceholder')" type="password" :show-password="true" clearable class="input-width" :readonly="confirmPasswordInput" @click="confirmPasswordInput = false" @blur="confirmPasswordInput = true" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('userCreateSiteLimit')" v-if="Object.keys(siteGroup).length" prop="create_site_limit">
|
||||
<el-form-item :label="t('userCreateSiteLimit')" v-if="!formData.uid && Object.keys(siteGroup).length" prop="create_site_limit">
|
||||
<div>
|
||||
<div>{{ t('siteGroup') }}</div>
|
||||
<el-checkbox-group v-model="formData.group_ids" @change="groupSelect">
|
||||
@ -60,75 +68,81 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import {computed, ref} from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { FormInstance } from 'element-plus'
|
||||
import { getSiteGroupAll } from '@/app/api/site'
|
||||
import { addUser } from '@/app/api/user'
|
||||
import { addUser, getUserInfo, editUser } from '@/app/api/user'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const loading = ref(true)
|
||||
const formData = ref({
|
||||
uid: 0,
|
||||
username: '',
|
||||
password: '',
|
||||
head_img: '',
|
||||
real_name: '',
|
||||
confirm_password: '',
|
||||
create_site_limit: [],
|
||||
group_ids: []
|
||||
})
|
||||
const siteGroup = ref({})
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRules = ref({
|
||||
username: [
|
||||
{ required: true, message: t('usernamePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: t('passwordPlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
real_name: [
|
||||
{ required: true, message: t('userRealNamePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
confirm_password: [
|
||||
{ required: true, message: t('confirmPasswordPlaceholder'), trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
if (value != formData.value.password) callback(new Error(t('confirmPasswordError')))
|
||||
else callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
create_site_limit: [
|
||||
{
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
let verify = true
|
||||
for (let i = 0; i < formData.value.create_site_limit.length; i++) {
|
||||
const item = formData.value.create_site_limit[i]
|
||||
if (Test.empty(item.num)) {
|
||||
callback(t('siteNumPlaceholder'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
if (item.num < 1) {
|
||||
callback(t('siteNumCannotLtOne'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
if (Test.empty(item.month)) {
|
||||
callback(t('siteMonthPlaceholder'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
if (item.month < 0) {
|
||||
callback(t('siteMonthCannotLtOne'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (verify) callback()
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
username: [
|
||||
{ required: true, message: t('usernamePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: formData.value.uid == 0, message: t('passwordPlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
real_name: [
|
||||
{ required: true, message: t('userRealNamePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
confirm_password: [
|
||||
{ required: formData.value.uid == 0, message: t('confirmPasswordPlaceholder'), trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
if (value != formData.value.password) callback(new Error(t('confirmPasswordError')))
|
||||
else callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
create_site_limit: [
|
||||
{
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
if (formData.value.uid) callback()
|
||||
let verify = true
|
||||
for (let i = 0; i < formData.value.create_site_limit.length; i++) {
|
||||
const item = formData.value.create_site_limit[i]
|
||||
if (Test.empty(item.num)) {
|
||||
callback(t('siteNumPlaceholder'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
if (item.num < 1) {
|
||||
callback(t('siteNumCannotLtOne'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
if (Test.empty(item.month)) {
|
||||
callback(t('siteMonthPlaceholder'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
if (item.month < 0) {
|
||||
callback(t('siteMonthCannotLtOne'))
|
||||
verify = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (verify) callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
getSiteGroupAll().then(({ data }) => {
|
||||
@ -141,7 +155,14 @@ getSiteGroupAll().then(({ data }) => {
|
||||
|
||||
const setFormData = (uid: number = 0) => {
|
||||
if (uid) {
|
||||
|
||||
getUserInfo(uid).then(({ data }) => {
|
||||
formData.value.uid = data.uid
|
||||
formData.value.username = data.username
|
||||
formData.value.real_name = data.real_name
|
||||
formData.value.head_img = data.head_img
|
||||
loading.value = false
|
||||
showDialog.value = true
|
||||
})
|
||||
} else {
|
||||
loading.value = false
|
||||
showDialog.value = true
|
||||
@ -172,7 +193,7 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
const save = addUser
|
||||
const save = formData.value.uid ? editUser : addUser
|
||||
|
||||
save(formData.value).then(() => {
|
||||
loading.value = false
|
||||
@ -185,9 +206,9 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
||||
})
|
||||
}
|
||||
|
||||
const real_name_input = ref(true)
|
||||
const password_input = ref(true)
|
||||
const confirm_password_input = ref(true)
|
||||
const realnameInput = ref(true)
|
||||
const passwordInput = ref(true)
|
||||
const confirmPasswordInput = ref(true)
|
||||
|
||||
defineExpose({
|
||||
showDialog,
|
||||
|
||||
@ -81,7 +81,7 @@
|
||||
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" @click="confirm(formRef)" v-loading="saveLoading">{{ t('save') }}</el-button>
|
||||
<el-button type="primary" @click="confirm(formRef)">{{ t('save') }}</el-button>
|
||||
<el-button @click="back()">{{ t('cancel') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="siteTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('siteName')" prop="keywords">
|
||||
<el-form-item :label="t('siteInfo')" prop="keywords">
|
||||
<el-input v-model="siteTableData.searchParam.keywords" :placeholder="t('siteNamePlaceholder')" />
|
||||
</el-form-item>
|
||||
|
||||
@ -157,6 +157,7 @@ import { useRouter, useRoute } from 'vue-router'
|
||||
import EditSite from '@/app/views/site/components/edit-site.vue'
|
||||
import { getInstalledAddonList } from '@/app/api/addon'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import {deleteUser} from "@/app/api/user";
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
@ -308,8 +309,16 @@ const toSiteLink = (siteId:number = 0) => {
|
||||
|
||||
const openClose = (i, site_id) => {
|
||||
if (i == 1) {
|
||||
closeSite({ site_id }).then(res => {
|
||||
loadSiteList()
|
||||
ElMessageBox.confirm(t('closeSiteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
closeSite({ site_id }).then(res => {
|
||||
loadSiteList()
|
||||
})
|
||||
})
|
||||
}
|
||||
if (i == 3) {
|
||||
|
||||
@ -65,10 +65,11 @@
|
||||
{{ row.last_ip || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="200">
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="detailEvent(row.uid)">{{ t('detail') }}</el-button>
|
||||
<template v-if="!row.is_super_admin">
|
||||
<el-button type="primary" link @click="editEvent(row.uid)" >{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="detailEvent(row.uid, 'userCreateSiteLimit')" >{{ t('userCreateSiteLimit') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.uid)" >{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
@ -157,6 +158,14 @@ const detailEvent = (uid: number, tab: string = '') => {
|
||||
router.push({ path: '/admin/site/user_info', query: { uid, tab } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户
|
||||
* @param uid
|
||||
*/
|
||||
const editEvent = (uid: number) => {
|
||||
userEditRef.value.setFormData(uid)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
|
||||
@ -22,6 +22,30 @@
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<el-card class="box-card !border-none mb-[10px] table-search-wrap" shadow="never">
|
||||
<div class="flex justify-between">
|
||||
<el-form :inline="true" :model="cronTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('title')" prop="key">
|
||||
<el-select v-model="cronTableData.searchParam.key" placeholder="全部" filterable remote clearable :remote-method="getAddonDevelopFn">
|
||||
<el-option label="全部" value="all" />
|
||||
<el-option v-for="item in templateList" :key="item.key" :label="item.name" :value="item.key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('status')" prop="status">
|
||||
<el-select v-model="cronTableData.searchParam.status" placeholder="全部" filterable remote clearable :remote-method="getAddonDevelopFn">
|
||||
<el-option label="全部" value="all" />
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="关闭" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadCronList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<div class="mt-[20px]">
|
||||
<el-table :data="cronTableData.data" size="large" v-loading="cronTableData.loading">
|
||||
<template #empty>
|
||||
@ -124,9 +148,8 @@ const cronTableData = reactive({
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
title: '',
|
||||
type: '',
|
||||
last_time: ''
|
||||
status: 'all'
|
||||
}
|
||||
})
|
||||
const templateList = ref([])
|
||||
@ -134,6 +157,12 @@ const date_type = ref([])
|
||||
const week_list = ref([])
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
loadCronList()
|
||||
}
|
||||
|
||||
const setTypeList = async () => {
|
||||
templateList.value = await (await getCronTemplate()).data
|
||||
date_type.value = await (await getCronDateType()).data
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="failReasonDialogShow" :title="t('failReason')" width="60%">
|
||||
<el-scrollbar class="h-[60vh] w-full whitespace-pre p-[20px]">
|
||||
<el-scrollbar class="h-[60vh] w-full whitespace-pre-wrap p-[20px]">
|
||||
{{ failReason }}
|
||||
</el-scrollbar>
|
||||
</el-dialog>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<upload-attachment type="image" ref="imageRef" :limit="10" @confirm="imageSelect" />
|
||||
<upload-attachment type="image" ref="imageRef" limit="" @confirm="imageSelect" />
|
||||
<upload-attachment type="video" ref="videoRef" @confirm="videoSelect" />
|
||||
<vue-ueditor-wrap v-model="content" :config="editorConfig" :editorDependencies="['ueditor.config.js','ueditor.all.js']" ref="editorRef"></vue-ueditor-wrap>
|
||||
</template>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog v-model="status" :title="t('exportTip')" width="300px" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
|
||||
<el-dialog v-model="status" :title="t('exportTip')" width="300px" :close-on-click-modal="true" :close-on-press-escape="false" :show-close="false">
|
||||
<span>{{ t('exportPlaceholder') }}</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@ -52,23 +52,25 @@ const router = useRouter()
|
||||
* 导出报表并跳转到下载页
|
||||
*/
|
||||
const detectionExportFn = () => {
|
||||
loading.value = true
|
||||
loading.value = true
|
||||
const url = router.resolve({
|
||||
path: '/site/setting/export'
|
||||
})
|
||||
exportDataCheck(prop.type, { page: 1, limit: 1, ...prop.searchParam }).then((res: any) => {
|
||||
if (res.data) {
|
||||
exportData(prop.type, prop.searchParam).then(() => {
|
||||
loading.value = false
|
||||
loading.value = false
|
||||
emit('close', false)
|
||||
setTimeout(() => {
|
||||
window.open(url.href)
|
||||
}, 100)
|
||||
})
|
||||
} else {
|
||||
emit('close', false)
|
||||
loading.value = false
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
// 关闭弹框
|
||||
|
||||
@ -113,7 +113,7 @@
|
||||
<div class="attachment-item mr-[10px] w-[120px]" v-for="(item, index) in attachment.data" :key="index">
|
||||
<div class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
|
||||
<el-image :src="img(item.url)" fit="contain" v-if="type == 'image'" :preview-src-list="item.image_list"/>
|
||||
<video :src="img(item.url)" v-else-if="type == 'video'"></video>
|
||||
<video :src="img(item.url)" v-else-if="type == 'video'" @click="previewVideo(index)"></video>
|
||||
<icon :name="item.url" size="24px" v-else-if="type == 'icon'"></icon>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
@ -197,7 +197,7 @@ import {
|
||||
getIconList
|
||||
} from '@/app/api/sys'
|
||||
import { debounce, img, getToken } from '@/utils/common'
|
||||
import { ElMessage, UploadFile, UploadFiles, ElMessageBox } from 'element-plus'
|
||||
import { ElMessage, UploadFile, UploadFiles, ElMessageBox, MessageParams } from 'element-plus'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const attachmentCategoryName = ref('')
|
||||
@ -382,12 +382,24 @@ const upload = computed(() => {
|
||||
uploadRef.value?.handleRemove(uploadFile)
|
||||
} else {
|
||||
uploadFile.status = 'fail'
|
||||
ElMessage({ message: response.msg, type: 'error' })
|
||||
showElMessage({ message: response.msg, type: 'error' })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const messageCache = new Map()
|
||||
|
||||
const showElMessage = (options: MessageParams) => {
|
||||
const cacheKey = options.message
|
||||
const cachedMessage = messageCache.get(cacheKey)
|
||||
|
||||
if (!cachedMessage || Date.now() - cachedMessage.timestamp > 5000) { // 5秒内重复内容不再弹出,可自定义过期时间
|
||||
messageCache.set(cacheKey, { timestamp: Date.now() })
|
||||
ElMessage(options)
|
||||
}
|
||||
}
|
||||
|
||||
// 全选
|
||||
const selectAll = ref(false)
|
||||
watch(selectAll, () => {
|
||||
@ -431,7 +443,7 @@ const selectFile = (data: any) => {
|
||||
if (prop.limit == 1 && length == prop.limit) {
|
||||
delete selectedFile[keys[0]]
|
||||
selectedFileIndex.splice(selectedFileIndex.indexOf(keys[0]),1);
|
||||
} else if (length >= prop.limit) {
|
||||
} else if (prop.limit && length >= prop.limit) {
|
||||
ElMessage.info(t('upload.triggerUpperLimit'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -18,6 +18,17 @@
|
||||
<el-color-picker v-model="theme" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 布局风格 -->
|
||||
<div class="setting-item mb-[10px]">
|
||||
<div class="title text-base text-tx-secondary">{{ t('layout.layoutStyle') }}</div>
|
||||
<div class="flex mt-[10px] layout-style flex-wrap">
|
||||
<div class="relative w-[125px] h-[100px] border mr-[10px] mb-[10px] hover:border-primary"
|
||||
:class="{ 'border-primary': currLayout == item.key }" v-for="(item, index) in layouts"
|
||||
@click="handleSetLayout(item.key)">
|
||||
<img :src="item.image" alt="" class="w-full h-full">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-drawer>
|
||||
</div>
|
||||
@ -27,8 +38,15 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { setThemeColor } from '@/utils/common'
|
||||
import { setThemeColor, img } from '@/utils/common'
|
||||
import { t } from '@/lang'
|
||||
import Storage from '@/utils/storage'
|
||||
|
||||
const layouts = ref([
|
||||
{ key: 'admin', image: img('static/resource/images/system/layout_bussiness.png') },
|
||||
{ key: 'admin_simplicity', image: img('static/resource/images/system/layout_darkside.png') }
|
||||
])
|
||||
const currLayout = ref(Storage.get('admin_layout') || 'admin')
|
||||
|
||||
const drawer = ref(false)
|
||||
const systemStore = useSystemStore()
|
||||
@ -66,6 +84,11 @@ const theme = computed({
|
||||
setThemeColor(systemStore.theme, systemStore.dark ? 'dark' : 'light')
|
||||
}
|
||||
})
|
||||
|
||||
const handleSetLayout = (key: string) => {
|
||||
Storage.set({ key: 'admin_layout', data: key })
|
||||
location.reload()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
65
admin/src/layout/admin_simplicity/components/aside/index.vue
Normal file
65
admin/src/layout/admin_simplicity/components/aside/index.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<el-aside class="layout-aside dark w-auto">
|
||||
<side class="hidden-xs-only slide" />
|
||||
</el-aside>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import side from './side.vue'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
|
||||
const systemStore = useSystemStore()
|
||||
const dark = computed(() => {
|
||||
return systemStore.dark
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
watch(route, () => {
|
||||
systemStore.$patch(state => {
|
||||
state.menuDrawer = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.layout-aside {
|
||||
//--side-dark-color: #141414;
|
||||
//background-color: var(--side-dark-color, var(--el-bg-color));
|
||||
|
||||
&.bright {
|
||||
background-color: #F5F7F9;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slide {
|
||||
border-right: 1px solid var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
|
||||
.aside-drawer {
|
||||
.el-drawer__body {
|
||||
padding: 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<template v-if="meta.show">
|
||||
<el-sub-menu v-if="meta.type == 0 && routes.children" :index="String(routes.name)">
|
||||
<template #title>
|
||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||
</div>
|
||||
<span class="ml-[10px]">{{ meta.title }}</span>
|
||||
</template>
|
||||
<menu-item v-for="(route, index) in routes.children" :routes="route" :key="index" :level="props.level + 1" />
|
||||
</el-sub-menu>
|
||||
<template v-else>
|
||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })">
|
||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||
</div>
|
||||
<template #title>
|
||||
<span class="ml-[10px]">{{ meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { img } from '@/utils/common'
|
||||
import menuItem from './menu-item.vue'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
const routers = useUserStore().routers
|
||||
const props = defineProps({
|
||||
routes: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
level: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
const systemStore = useSystemStore()
|
||||
const meta = computed(() => props.routes.meta)
|
||||
|
||||
const addons = computed(() => {
|
||||
const addons:Record<string, any> = {}
|
||||
userStore.siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
||||
userStore.siteInfo?.site_addons.forEach((item: any) => { addons[item.key] = item })
|
||||
return addons
|
||||
})
|
||||
|
||||
const systemAddonKeys = computed(() => {
|
||||
return userStore.siteInfo?.site_addons.map((item: any) => item.key)
|
||||
})
|
||||
|
||||
const addonRouters: Record<string, any> = {}
|
||||
routers.forEach(item => {
|
||||
item.original_name = item.name
|
||||
if (item.meta.addon) {
|
||||
addonRouters[item.meta.addon] = item
|
||||
}
|
||||
})
|
||||
|
||||
const addonsMenus = ref(null)
|
||||
|
||||
watch(route, () => {
|
||||
if (props.routes.name != 'addon_list') return
|
||||
|
||||
if (systemAddonKeys.value.includes(route.meta.addon) && addonRouters[route.meta.addon]) {
|
||||
addonsMenus.value = addonRouters[route.meta.addon]
|
||||
} else {
|
||||
addonsMenus.value = null
|
||||
}
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.el-sub-menu{
|
||||
.el-icon{
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
117
admin/src/layout/admin_simplicity/components/aside/side.vue
Normal file
117
admin/src/layout/admin_simplicity/components/aside/side.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<el-container class="h-screen flex flex-col">
|
||||
<el-main class="menu-wrap">
|
||||
<el-header class="logo-wrap flex items-center justify-center h-[64px] w-[var(--aside-width)]">
|
||||
<div class="flex justify-center items-center h-[64px] w-full px-[10px]" v-if="Object.keys(website).length">
|
||||
<el-image :src="img(website.icon)" class="w-[44px] h-[44px] rounded-[50%]" @error="website.icon = img('static/resource/images/niucloud_icon.jpg')"></el-image>
|
||||
<div class="flex-1 w-0 overflow-text truncate ml-[10px] text-white" v-if="!systemStore.menuIsCollapse">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="website.site_name"
|
||||
placement="top"
|
||||
>
|
||||
{{ website.site_name }}
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-scrollbar class="menu-scrollbar flex-1 h-0">
|
||||
<el-menu :default-active="route.name" :router="true" :unique-opened="false" :collapse="systemStore.menuIsCollapse" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
|
||||
<menu-item v-for="(route, index) in menuData" :routes="route" :key="index" />
|
||||
</el-menu>
|
||||
<div class="h-[48px]"></div>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import menuItem from './menu-item.vue'
|
||||
import { img, isUrl } from '@/utils/common'
|
||||
import { findFirstValidRoute } from '@/router/routers'
|
||||
|
||||
const systemStore = useSystemStore()
|
||||
const userStore = useUserStore()
|
||||
const route = useRoute()
|
||||
const siteInfo = userStore.siteInfo
|
||||
const routers = userStore.routers
|
||||
const addonIndexRoute = userStore.addonIndexRoute
|
||||
const menuData = ref<Record<string, any>[]>([])
|
||||
const addonRouters: Record<string, any> = {}
|
||||
const website = computed(() => {
|
||||
return systemStore.website
|
||||
})
|
||||
|
||||
routers.forEach(item => {
|
||||
item.original_name = item.name
|
||||
if (item.meta.addon == '') {
|
||||
if (item.children && item.children.length) {
|
||||
item.name = findFirstValidRoute(item.children)
|
||||
}
|
||||
menuData.value.push(item)
|
||||
} else if (item.meta.addon != '' && siteInfo?.apps.length == 1 && siteInfo?.apps[0].key == item.meta.addon) {
|
||||
if (item.children) {
|
||||
item.children.forEach((citem: Record<string, any>) => {
|
||||
citem.original_name = citem.name
|
||||
if (citem.children && citem.children.length) {
|
||||
citem.name = findFirstValidRoute(citem.children)
|
||||
}
|
||||
})
|
||||
menuData.value.unshift(...item.children)
|
||||
} else {
|
||||
menuData.value.unshift(item)
|
||||
}
|
||||
} else {
|
||||
addonRouters[item.meta.addon] = item
|
||||
}
|
||||
})
|
||||
|
||||
// 多应用时将应用插入菜单
|
||||
if (siteInfo?.apps.length > 1) {
|
||||
const routers:Record<string, any>[] = []
|
||||
siteInfo?.apps.forEach((item: Record<string, any>) => {
|
||||
if (addonRouters[item.key]) {
|
||||
addonRouters[item.key].name = addonIndexRoute[item.key]
|
||||
routers.push(addonRouters[item.key])
|
||||
}
|
||||
})
|
||||
menuData.value.unshift(...routers)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.logo-wrap {
|
||||
background: #545c64;
|
||||
transition: transform getCssVar('transition-duration');
|
||||
}
|
||||
:root{
|
||||
--aside-width: 200px;
|
||||
}
|
||||
.menu-wrap {
|
||||
padding: 0!important;
|
||||
background: #545c64;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-menu {
|
||||
border-right: 0!important;
|
||||
|
||||
&:not(.el-menu--collapse) {
|
||||
width: var(--aside-width);
|
||||
}
|
||||
|
||||
.el-menu-item, .el-sub-menu__title {
|
||||
--el-menu-item-height: 40px;
|
||||
}
|
||||
|
||||
.el-sub-menu .el-menu-item {
|
||||
--el-menu-sub-item-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
140
admin/src/layout/admin_simplicity/components/header/index.vue
Normal file
140
admin/src/layout/admin_simplicity/components/header/index.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<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="left-panel flex items-center text-[14px] leading-[1]">
|
||||
<div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleMenuCollapse">
|
||||
<icon name="element Expand" v-if="systemStore.menuIsCollapse" />
|
||||
<icon name="element Fold" v-else />
|
||||
</div>
|
||||
<!-- 刷新当前页 -->
|
||||
<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">
|
||||
<layout-setting />
|
||||
</div>
|
||||
<!-- 用户信息 -->
|
||||
<div class="navbar-item flex items-center h-full cursor-pointer">
|
||||
<user-info />
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" v-model="comparisonToken">
|
||||
<input type="hidden" v-model="comparisonSiteId">
|
||||
|
||||
<el-dialog v-model="detectionLoginDialog" :title="t('layout.detectionLoginTip')" width="30%" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
|
||||
<span>{{ t('layout.detectionLoginContent') }}</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="detectionLoginFn">{{ t('layout.detectionLoginOperation') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="showDialog" :title="t('indexTemplate')" width="550px" :destroy-on-close="true" >
|
||||
<div class="flex flex-wrap">
|
||||
<div v-for="(items, index) in indexList" :key="index" v-if="index_path == ''">
|
||||
<div @click="index_path = items.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="items.is_use == 1 ? 'bg-primary text-[#fff]' : '' ">
|
||||
<span >{{ items.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(itemTo, indexTo) in indexList" :key="indexTo" v-else>
|
||||
<div @click="index_path = itemTo.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="index_path == itemTo.view_path ? 'bg-primary text-[#fff]' : '' ">
|
||||
<span >{{ itemTo.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="submitIndex">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { t } from '@/lang'
|
||||
import storage from '@/utils/storage'
|
||||
import userInfo from './user-info.vue'
|
||||
import layoutSetting from './layout-setting.vue'
|
||||
import useSystemStore from "@/stores/modules/system";
|
||||
|
||||
const route = useRoute()
|
||||
const appStore = useAppStore()
|
||||
const userStore = useUserStore()
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
// 检测登录 start
|
||||
const detectionLoginDialog = ref(false)
|
||||
const comparisonToken = ref('')
|
||||
const comparisonSiteId = ref('')
|
||||
if (storage.get('comparisonTokenStorage')) {
|
||||
comparisonToken.value = storage.get('comparisonTokenStorage')
|
||||
}
|
||||
if (storage.get('comparisonSiteIdStorage')) {
|
||||
comparisonSiteId.value = storage.get('comparisonSiteIdStorage')
|
||||
}
|
||||
// 监听标签页面切换
|
||||
document.addEventListener('visibilitychange', e => {
|
||||
if (document.visibilityState === 'visible' && (comparisonSiteId.value != storage.get('siteId') || comparisonToken.value != storage.get('token'))) {
|
||||
detectionLoginDialog.value = true
|
||||
}
|
||||
})
|
||||
|
||||
systemStore.toggleMenuCollapse(storage.get('menuiscollapse') || false)
|
||||
|
||||
const detectionLoginFn = () => {
|
||||
detectionLoginDialog.value = false
|
||||
location.reload()
|
||||
}
|
||||
// 检测登录 end
|
||||
|
||||
// 刷新路由
|
||||
const refreshRouter = () => {
|
||||
if (!appStore.routeRefreshTag) return
|
||||
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: "" })
|
||||
|
||||
const toggleMenuCollapse = () => {
|
||||
systemStore.toggleMenuCollapse(!systemStore.menuIsCollapse)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-header{
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
border-bottom: 1px solid #e8e9eb;
|
||||
}
|
||||
.navbar-item {
|
||||
padding: 0 8px;
|
||||
}
|
||||
.index-item {
|
||||
border: 1px solid;
|
||||
border-color: var(--el-color-primary);
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<icon name="element Setting" @click="drawer = true" />
|
||||
|
||||
<el-drawer v-model="drawer" :title="t('layout.layoutSetting')" size="300px">
|
||||
<el-scrollbar>
|
||||
<!-- 黑暗模式 -->
|
||||
<div class="setting-item flex items-center justify-between mb-[10px]">
|
||||
<div class="title text-base text-tx-secondary">{{ t('layout.darkMode') }}</div>
|
||||
<div>
|
||||
<el-switch v-model="dark" :active-value="true" :inactive-value="false" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 主题颜色 -->
|
||||
<div class="setting-item flex items-center justify-between mb-[10px]">
|
||||
<div class="title text-base text-tx-secondary">{{ t('layout.themeColor') }}</div>
|
||||
<div>
|
||||
<el-color-picker v-model="theme" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 布局风格 -->
|
||||
<div class="setting-item mb-[10px]">
|
||||
<div class="title text-base text-tx-secondary">{{ t('layout.layoutStyle') }}</div>
|
||||
<div class="flex mt-[10px] layout-style flex-wrap">
|
||||
<div class="relative w-[125px] h-[100px] border mr-[10px] mb-[10px] hover:border-primary"
|
||||
:class="{ 'border-primary': currLayout == item.key }" v-for="(item, index) in layouts"
|
||||
@click="handleSetLayout(item.key)">
|
||||
<img :src="item.image" alt="" class="w-full h-full">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { setThemeColor, img } from '@/utils/common'
|
||||
import { t } from '@/lang'
|
||||
import Storage from '@/utils/storage'
|
||||
|
||||
const layouts = ref([
|
||||
{ key: 'admin', image: img('static/resource/images/system/layout_bussiness.png') },
|
||||
{ key: 'admin_simplicity', image: img('static/resource/images/system/layout_darkside.png') }
|
||||
])
|
||||
const currLayout = ref(Storage.get('admin_layout') || 'admin')
|
||||
|
||||
const drawer = ref(false)
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
const isDark = useDark()
|
||||
const toggleDark = useToggle(isDark)
|
||||
|
||||
const dark = computed({
|
||||
get() {
|
||||
return systemStore.dark
|
||||
},
|
||||
set(val) {
|
||||
systemStore.setTheme('dark', val)
|
||||
toggleDark(val)
|
||||
setThemeColor(systemStore.theme, systemStore.dark ? 'dark' : 'light')
|
||||
}
|
||||
})
|
||||
|
||||
const sidebar = computed({
|
||||
get() {
|
||||
return systemStore.sidebar
|
||||
},
|
||||
set(val) {
|
||||
systemStore.setTheme('sidebar', val)
|
||||
setThemeColor(systemStore.theme, systemStore.dark ? 'dark' : 'light')
|
||||
}
|
||||
})
|
||||
|
||||
const theme = computed({
|
||||
get() {
|
||||
return systemStore.theme
|
||||
},
|
||||
set(val) {
|
||||
systemStore.setTheme('theme', val)
|
||||
setThemeColor(systemStore.theme, systemStore.dark ? 'dark' : 'light')
|
||||
}
|
||||
})
|
||||
|
||||
const handleSetLayout = (key: string) => {
|
||||
Storage.set({ key: 'admin_layout', data: key })
|
||||
location.reload()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-drawer__header) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.layout-style {
|
||||
&>div:nth-child(2n+2) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<el-dropdown @command="switchLang" :tabindex="1">
|
||||
<icon name="iconfont iconfanyi" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="zh-cn" :disabled="systemStore.lang == 'zh-cn'">简体中文</el-dropdown-item>
|
||||
<el-dropdown-item command="en" :disabled="systemStore.lang == 'en'">English</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import { language } from '@/lang'
|
||||
import { useRoute } from 'vue-router'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const route = useRoute()
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
const switchLang = (command: string) => {
|
||||
systemStore.$patch((state) => {
|
||||
state.lang = command
|
||||
storage.set({ key: 'lang', data: command })
|
||||
})
|
||||
language.loadLocaleMessages(route.path, systemStore.lang)
|
||||
location.reload()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dropdown @command="clickEvent" :tabindex="1">
|
||||
<div class="userinfo flex h-full items-center">
|
||||
<el-avatar v-if="userStore.userInfo.head_img" :size="25" :icon="UserFilled" :src="img(userStore.userInfo.head_img)"/>
|
||||
<img v-else src="@/app/assets/images/member_head.png" class="w-[25px] rounded-full" />
|
||||
<div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div>
|
||||
<icon name="element ArrowDown" class="ml-[5px]" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<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>
|
||||
<router-link to="/tools/authorize">
|
||||
<div class="flex items-center leading-[1] py-[5px]">
|
||||
<span class="iconfont iconshouquanxinxi2 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]">
|
||||
<span class="iconfont iconxiugai ml-[4px] !text-[14px] mr-[10px]"></span>
|
||||
<span class="text-[14px]">修改密码</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<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="修改密码">
|
||||
<div>
|
||||
<el-form :model="saveInfo" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
|
||||
<el-form-item :label="t('originalPassword')" prop="original_password">
|
||||
<el-input v-model="saveInfo.original_password" type="password" :placeholder="t('originalPasswordPlaceholder')" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('newPassword')" prop="password">
|
||||
<el-input v-model="saveInfo.password" type="password" :placeholder="t('passwordPlaceholder')" clearable class="input-width" />
|
||||
<div class="form-tip">{{t('passwordTip')}}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('passwordCopy')" prop="password_copy">
|
||||
<el-input v-model="saveInfo.password_copy" type="password" :placeholder="t('passwordPlaceholder')" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="changePasswordDialog = false">{{t('cancel')}}</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)">{{t('save')}}</el-button>
|
||||
</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 type { FormInstance, FormRules, ElNotification } from 'element-plus'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { setUserInfo } from '@/app/api/personal'
|
||||
import { t } from '@/lang'
|
||||
import { img } from '@/utils/common'
|
||||
import userInfoEdit from '@/app/components/user-info-edit/index.vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const clickEvent = (command: string) => {
|
||||
switch (command) {
|
||||
case 'logout':
|
||||
userStore.logout()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
userStore.logout();
|
||||
}
|
||||
const userInfoEditRef = ref(null)
|
||||
const getUserInfoFn = ()=>{
|
||||
userInfoEditRef.value?.open()
|
||||
}
|
||||
// 修改密码 --- start
|
||||
const changePasswordDialog = ref(false)
|
||||
const formRef = ref<FormInstance>();
|
||||
// 提交信息
|
||||
const 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" },
|
||||
]
|
||||
});
|
||||
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');
|
||||
if (msg) {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: msg,
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
setUserInfo(saveInfo).then((res: any) => {
|
||||
changePasswordDialog.value = false;
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-popper .el-dropdown-menu{
|
||||
width: 150px;
|
||||
}
|
||||
</style>
|
||||
136
admin/src/layout/admin_simplicity/components/tabs.vue
Normal file
136
admin/src/layout/admin_simplicity/components/tabs.vue
Normal file
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div class="tab-wrap w-full px-[16px]">
|
||||
<el-tabs :closable="tabbarStore.tabLength > 1" :model-value="route.path" @tab-click="tabClick" @tab-remove="removeTab">
|
||||
<el-tab-pane v-for="(tab, key, index) in tabbarStore.tabs" :name="tab.path" :key="index">
|
||||
<template #label>
|
||||
<el-dropdown trigger="contextmenu" placement="bottom-start">
|
||||
<span :class="{ 'text-primary': route.path == tab.path }" class="tab-name">{{ tab.title }}</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Back" :disabled="index == 0" @click="closeLeft(tab.path)">{{t('tabs.closeLeft') }}</el-dropdown-item>
|
||||
<el-dropdown-item icon="Right" :disabled="index == (tabbarStore.tabLength - 1)" @click="closeRight(tab.path)">{{t('tabs.closeRight') }}</el-dropdown-item>
|
||||
<el-dropdown-item icon="Close" :disabled="tabbarStore.tabLength == 1" @click="closeOther(tab.path)">{{t('tabs.closeOther') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, onMounted } from 'vue'
|
||||
import useTabbarStore from '@/stores/modules/tabbar'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { t } from '@/lang'
|
||||
|
||||
const tabbarStore = useTabbarStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
tabbarStore.addTab(route)
|
||||
})
|
||||
|
||||
watch(route, (nval: any) => {
|
||||
tabbarStore.addTab(nval)
|
||||
})
|
||||
|
||||
/**
|
||||
* 添加tab
|
||||
* @param content
|
||||
*/
|
||||
const tabClick = (content: any) => {
|
||||
const tabRoute = tabbarStore.tabs[content.props.name]
|
||||
router.push({ path: tabRoute.path, query: tabRoute.query })
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除tab
|
||||
* @param content
|
||||
*/
|
||||
const removeTab = (content: any) => {
|
||||
if (route.path == content) {
|
||||
const tabs = Object.keys(tabbarStore.tabs)
|
||||
router.push({ path: tabs[tabs.indexOf(content) - 1] })
|
||||
}
|
||||
tabbarStore.removeTab(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭左侧
|
||||
* @param path
|
||||
*/
|
||||
const closeLeft = (path: string) => {
|
||||
const tabs = Object.keys(tabbarStore.tabs)
|
||||
for (let i = tabs.indexOf(path) - 1; i >= 0; i--) {
|
||||
delete tabbarStore.tabs[tabs[i]]
|
||||
}
|
||||
router.push({ path })
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭右侧
|
||||
* @param path
|
||||
*/
|
||||
const closeRight = (path: string) => {
|
||||
const tabs = Object.keys(tabbarStore.tabs)
|
||||
for (let i = tabs.indexOf(path) + 1; i < tabs.length; i++) {
|
||||
delete tabbarStore.tabs[tabs[i]]
|
||||
}
|
||||
router.push({ path })
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭其他
|
||||
* @param path
|
||||
*/
|
||||
const closeOther = (path: string) => {
|
||||
const tabs = Object.keys(tabbarStore.tabs)
|
||||
tabs.forEach((key: string) => { key != path && delete tabbarStore.tabs[key] })
|
||||
router.push({ path })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs) {
|
||||
.el-tabs--border-card {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.el-tabs__header {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap {
|
||||
margin-bottom: 0;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
display: inline-flex !important;
|
||||
padding: 0 20px !important;
|
||||
align-items: center;
|
||||
|
||||
.tab-name:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__active-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
41
admin/src/layout/admin_simplicity/index.vue
Normal file
41
admin/src/layout/admin_simplicity/index.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="flex w-full h-screen">
|
||||
<!-- 左侧边栏 -->
|
||||
<layout-aside></layout-aside>
|
||||
<!-- 左侧边栏 end -->
|
||||
|
||||
<el-container>
|
||||
<!-- 顶部 -->
|
||||
<el-header>
|
||||
<layout-header></layout-header>
|
||||
</el-header>
|
||||
<!-- 顶部 end -->
|
||||
|
||||
<!-- 主体 -->
|
||||
<el-main class="h-full p-0 bg-page">
|
||||
<el-scrollbar>
|
||||
<div class="p-[15px]">
|
||||
<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>
|
||||
<!-- 主体 end -->
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
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'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const tabbarStore = useTabbarStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -7,6 +7,7 @@ import { ref, markRaw, defineAsyncComponent, provide } from 'vue'
|
||||
import { getAppType } from '@/utils/common'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import useSystemStore from '@/stores/modules/system'
|
||||
import Storage from '@/utils/storage'
|
||||
|
||||
const sysLayout = import.meta.glob('./*/index.vue')
|
||||
const addonLayout = import.meta.glob('@/addon/**/layout/index.vue')
|
||||
@ -15,7 +16,7 @@ const modules = Object.assign(sysLayout, addonLayout)
|
||||
let siteLayout = 'default'
|
||||
switch (getAppType()) {
|
||||
case 'admin':
|
||||
siteLayout = 'admin'
|
||||
siteLayout = Storage.get('admin_layout') || 'admin'
|
||||
break
|
||||
default:
|
||||
const siteInfo = useUserStore().siteInfo
|
||||
|
||||
@ -90,7 +90,7 @@ html, body {
|
||||
|
||||
.fixed-footer {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
z-index: 4;
|
||||
right: 15px;
|
||||
bottom: 0;
|
||||
left: 15px;
|
||||
|
||||
@ -1 +1 @@
|
||||
/* addon-iconfont.css */
|
||||
/* addon iconfont */
|
||||
|
||||
0
niucloud/addon/.gitignore
vendored
0
niucloud/addon/.gitignore
vendored
@ -106,7 +106,7 @@ class User extends BaseAdminController
|
||||
*/
|
||||
public function del($uid)
|
||||
{
|
||||
(new UserService())->del($uid);
|
||||
(new SiteUserService())->del($uid);
|
||||
return success('DELETE_SUCCESS');
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ class Schedule extends BaseAdminController
|
||||
{
|
||||
$data = $this->request->params([
|
||||
['key', ''],
|
||||
['status', ''],
|
||||
['status', 'all'],
|
||||
]);
|
||||
return success(data: (new ScheduleService())->getPage($data));
|
||||
|
||||
|
||||
@ -86,6 +86,21 @@ class User extends BaseAdminController
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function edit($uid) {
|
||||
$data = $this->request->params([
|
||||
['password', ''],
|
||||
['real_name', ''],
|
||||
['head_img', ''],
|
||||
]);
|
||||
(new UserService())->edit($uid, $data);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @param $uid
|
||||
|
||||
@ -67,6 +67,8 @@ Route::group('site', function () {
|
||||
Route::put('user/:uid', 'site.User/edit');
|
||||
//站点修改用户属性
|
||||
Route::put('user/:uid/:field', 'site.User/modify');
|
||||
//站点删除用户
|
||||
Route::delete('user/:uid', 'site.User/del');
|
||||
/***************************************************** 操作日志 **************************************************/
|
||||
//操作日志列表
|
||||
Route::get('log', 'site.UserLog/lists');
|
||||
|
||||
@ -34,6 +34,8 @@ Route::group('user', function () {
|
||||
Route::get('isexist', 'user.User/checkUserIsExist');
|
||||
//添加用户
|
||||
Route::post('user', 'user.User/add');
|
||||
// 编辑用户
|
||||
Route::put('user/:uid', 'user.User/edit');
|
||||
// 获取用户站点创建限制
|
||||
Route::get('user/create_site_limit/:uid', 'user.User/getUserCreateSiteLimit');
|
||||
// 获取用户站点创建限制
|
||||
|
||||
@ -43,18 +43,18 @@ class ApiCheckToken
|
||||
try {
|
||||
$token = $request->apiToken();
|
||||
$token_info = ( new LoginService() )->parseToken($token);
|
||||
if (!empty($token_info)) {
|
||||
$request->memberId($token_info[ 'member_id' ]);
|
||||
}
|
||||
//校验会员和站点
|
||||
( new AuthService() )->checkSiteAuth($request);
|
||||
// 校验渠道
|
||||
( new AuthService() )->checkChannel($request);
|
||||
} catch (AuthException $e) {
|
||||
//是否将登录错误抛出
|
||||
if ($is_throw_exception)
|
||||
return fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
if (!empty($token_info)) {
|
||||
$request->memberId($token_info[ 'member_id' ]);
|
||||
}
|
||||
//校验会员和站点
|
||||
( new AuthService() )->checkSiteAuth($request);
|
||||
// 校验渠道
|
||||
( new AuthService() )->checkChannel($request);
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
29
niucloud/app/command/Resetpassword.php
Normal file
29
niucloud/app/command/Resetpassword.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use app\service\admin\auth\LoginService;
|
||||
use app\service\admin\install\InstallSystemService;
|
||||
use app\service\core\menu\CoreMenuService;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
|
||||
class Resetpassword extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('reset')
|
||||
->setDescription('the reset administrator password command');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
LoginService::resetAdministratorPassword();
|
||||
// 指令输出
|
||||
$output->writeln('password reset success');
|
||||
}
|
||||
}
|
||||
@ -69,10 +69,10 @@ function get_lang($str)
|
||||
function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = 'child', $root = 0)
|
||||
{
|
||||
// 创建Tree
|
||||
$tree = array ();
|
||||
$tree = array();
|
||||
if (is_array($list)) {
|
||||
// 创建基于主键的数组引用
|
||||
$refer = array ();
|
||||
$refer = array();
|
||||
foreach ($list as $key => $data) {
|
||||
$refer[ $data[ $pk ] ] =& $list[ $key ];
|
||||
}
|
||||
@ -132,7 +132,7 @@ function array_keys_search($array, $keys, $index = '', $is_sort = true)
|
||||
return [];
|
||||
if (!empty($index) && count($array) != count($array, COUNT_RECURSIVE))
|
||||
$array = array_column($array, null, $index);
|
||||
$list = array ();
|
||||
$list = array();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (isset($array[ $key ])) {
|
||||
@ -502,7 +502,7 @@ function array_merge2(array $array1, array $array2)
|
||||
function get_files_by_dir($dir)
|
||||
{
|
||||
$dh = @opendir($dir); //打开目录,返回一个目录流
|
||||
$return = array ();
|
||||
$return = array();
|
||||
while ($file = @readdir($dh)) { //循环读取目录下的文件
|
||||
if ($file != '.' and $file != '..') {
|
||||
$path = $dir . DIRECTORY_SEPARATOR . $file; //设置目录,用于含有子目录的情况
|
||||
@ -947,3 +947,18 @@ function str_sub($str, $length = 10, $is_need_apostrophe = true)
|
||||
{
|
||||
return mb_substr($str, 0, $length, 'UTF-8') . ( $is_need_apostrophe ? '...' : '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用正则表达式匹配特殊字符
|
||||
* @param $str
|
||||
* @return bool
|
||||
*/
|
||||
function is_special_character($str)
|
||||
{
|
||||
$pattern = '/[!@#$%^&*()\[\]{}<>\|?:;"]/';
|
||||
if (preg_match($pattern, $str)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ class CommonDict
|
||||
public const UNKNOWN = 0;
|
||||
public const MAN = 1;
|
||||
public const WOMAN = 2;
|
||||
|
||||
public const ENCRYPT_STR = '*****************************';
|
||||
|
||||
/**
|
||||
* 性别
|
||||
@ -26,4 +26,4 @@ class CommonDict
|
||||
self::WOMAN => get_lang('dict_sex.woman'),//女
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,6 +233,20 @@ return [
|
||||
'sort' => '100',
|
||||
'status' => '1',
|
||||
'is_show' => '1',
|
||||
],
|
||||
[
|
||||
'menu_name' => '删除管理员',
|
||||
'menu_key' => 'delete_site_user',
|
||||
'menu_short_name' => '删除管理员',
|
||||
'menu_type' => '2',
|
||||
'icon' => '',
|
||||
'api_url' => 'site/user/<uid>',
|
||||
'router_path' => '',
|
||||
'view_path' => '',
|
||||
'methods' => 'delete',
|
||||
'sort' => '100',
|
||||
'status' => '1',
|
||||
'is_show' => '1',
|
||||
]
|
||||
]
|
||||
],
|
||||
@ -828,7 +842,7 @@ return [
|
||||
],
|
||||
],
|
||||
[
|
||||
'menu_name' => '用户',
|
||||
'menu_name' => '用户管理',
|
||||
'menu_key' => 'site_user_list',
|
||||
'menu_short_name' => '用户',
|
||||
'menu_type' => '1',
|
||||
@ -1043,7 +1057,7 @@ return [
|
||||
]
|
||||
],
|
||||
[
|
||||
'menu_name' => '开发',
|
||||
'menu_name' => '开发管理',
|
||||
'menu_key' => 'app_manage_tool',
|
||||
'menu_short_name' => '开发',
|
||||
'menu_type' => '1',
|
||||
|
||||
@ -1618,6 +1618,20 @@ return [
|
||||
'sort' => '100',
|
||||
'status' => '1',
|
||||
'is_show' => '1',
|
||||
],
|
||||
[
|
||||
'menu_name' => '删除管理员',
|
||||
'menu_key' => 'delete_site_user',
|
||||
'menu_short_name' => '删除管理员',
|
||||
'menu_type' => '2',
|
||||
'icon' => '',
|
||||
'api_url' => 'site/user/<uid>',
|
||||
'router_path' => '',
|
||||
'view_path' => '',
|
||||
'methods' => 'delete',
|
||||
'sort' => '100',
|
||||
'status' => '1',
|
||||
'is_show' => '1',
|
||||
]
|
||||
]
|
||||
],
|
||||
|
||||
@ -57,19 +57,22 @@ class PayDict
|
||||
'name' => get_lang('dict_pay.type_wechatpay'),
|
||||
'key' => self::WECHATPAY,
|
||||
'icon' => self::WECHATPAY_ICON,
|
||||
'setting_component' => '/src/app/views/setting/components/pay-wechatpay.vue'
|
||||
'setting_component' => '/src/app/views/setting/components/pay-wechatpay.vue',
|
||||
'encrypt_params' => ['mch_public_cert_path', 'mch_secret_cert', 'mch_secret_key'],
|
||||
],//微信支付
|
||||
self::ALIPAY => [
|
||||
'name' => get_lang('dict_pay.type_alipay'),
|
||||
'key' => self::ALIPAY,
|
||||
'icon' => self::ALIPAY_ICON,
|
||||
'setting_component' => '/src/app/views/setting/components/pay-alipay.vue'
|
||||
'setting_component' => '/src/app/views/setting/components/pay-alipay.vue',
|
||||
'encrypt_params' => ['app_secret_cert', 'app_public_cert_path', 'alipay_public_cert_path', 'alipay_root_cert_path'],
|
||||
],//支付宝支付
|
||||
self::BALANCEPAY => [
|
||||
'name' => get_lang('dict_pay.type_balancepay'),
|
||||
'key' => self::BALANCEPAY,
|
||||
'icon' => self::BALANCEPAY_ICON,
|
||||
'setting_component' => ''
|
||||
'setting_component' => '',
|
||||
'encrypt_params' => ['secret_key'],
|
||||
],//微信支付
|
||||
];
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ class SmsDict
|
||||
'app_key' => 'APP_KEY',
|
||||
'secret_key' => 'SECRET_KEY'
|
||||
],
|
||||
'encrypt_params' => ['secret_key'],
|
||||
'component' => '/src/app/views/setting/components/sms-ali.vue',
|
||||
],
|
||||
self::TENCENTSMS => [
|
||||
@ -58,6 +59,7 @@ class SmsDict
|
||||
'secret_id' => 'SECRET_ID',
|
||||
'secret_key' => 'SECRET_KEY'
|
||||
],
|
||||
'encrypt_params' => ['secret_key'],
|
||||
'component' => '/src/app/views/setting/components/sms-tencent.vue',
|
||||
],
|
||||
];
|
||||
|
||||
@ -54,6 +54,7 @@ class StorageDict
|
||||
'secret_key' => 'SECRET_KEY',
|
||||
'domain' => '空间域名'
|
||||
],
|
||||
'encrypt_params' => ['secret_key'],
|
||||
'component' => '/src/app/views/setting/components/storage-qiniu.vue',
|
||||
],
|
||||
|
||||
@ -67,6 +68,7 @@ class StorageDict
|
||||
'endpoint' => 'Endpoint',
|
||||
'domain' => '空间域名'
|
||||
],
|
||||
'encrypt_params' => ['secret_key'],
|
||||
'component' => '/src/app/views/setting/components/storage-ali.vue',
|
||||
],
|
||||
|
||||
@ -80,6 +82,7 @@ class StorageDict
|
||||
'secret_key' => 'SECRET_KEY',
|
||||
'domain' => '空间域名'
|
||||
],
|
||||
'encrypt_params' => ['secret_key'],
|
||||
'component' => '/src/app/views/setting/components/storage-tencent.vue',
|
||||
],
|
||||
|
||||
|
||||
@ -923,6 +923,7 @@ CREATE TABLE `sys_user_role` (
|
||||
`create_time` int(11) NOT NULL DEFAULT 0 COMMENT '添加时间',
|
||||
`is_admin` int(11) NOT NULL DEFAULT 0 COMMENT '是否是超级管理员',
|
||||
`status` int(11) NOT NULL DEFAULT 1 COMMENT '状态',
|
||||
`delete_time` INT(11) NOT NULL DEFAULT 0 COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户权限表' ROW_FORMAT = Dynamic;
|
||||
|
||||
@ -1036,7 +1037,7 @@ UPDATE `site` SET site_id = 0 WHERE site_id = 1;
|
||||
|
||||
INSERT INTO `sys_user` VALUES ('1', '', '', '', '', '', '0', '0', '0', '1', '0', '0', '0');
|
||||
|
||||
INSERT INTO `sys_user_role` VALUES ('1', '1', '0', '', '0', '1', '1');
|
||||
INSERT INTO `sys_user_role` VALUES ('1', '1', '0', '', '0', '1', '1', '0');
|
||||
|
||||
INSERT INTO `sys_area` VALUES
|
||||
(110000, 0, '北京市', '北京', '116.40529', '39.904987', 1, 0, 1),
|
||||
|
||||
@ -84,6 +84,7 @@ return [
|
||||
//插件安装相关
|
||||
'REPEAT_INSTALL' => '当前插件已安装,不能重复安装',
|
||||
'NOT_UNINSTALL' => '当前插件未安装,不能进行卸载操作',
|
||||
'ADDON_INFO_FILE_NOT_EXIST' => '未找到插件的info.json文件',
|
||||
|
||||
//菜单管理
|
||||
'MENU_NOT_EXIST' => '菜单不存在',
|
||||
@ -183,6 +184,7 @@ return [
|
||||
'KEYWORDS_NOT_EXIST' => '关键词回复不存在',
|
||||
'WECHAT_EMPOWER_NOT_EXIST' => '微信授权信息不存在',
|
||||
'SCAN_SUCCESS' => '扫码成功',
|
||||
'WECHAT_SNAPSHOUTUSER' => '返回的是虚拟账号',
|
||||
//小程序
|
||||
'WEAPP_NOT_EXIST' => '微信小程序未配置完善',
|
||||
'WEAPP_EMPOWER_NOT_EXIST' => '微信小程序授信信息不存在',
|
||||
|
||||
@ -73,7 +73,7 @@ class Site extends BaseModel
|
||||
public function searchKeywordsAttr($query, $value, $data)
|
||||
{
|
||||
if ($value != '') {
|
||||
$query->where('site_name|keywords', 'like', '%' . $this->handelSpecialCharacter($value) . '%');
|
||||
$query->where('site_id|site_name|keywords', 'like', '%' . $this->handelSpecialCharacter($value) . '%');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ class SysSchedule extends BaseModel
|
||||
*/
|
||||
public function searchStatusAttr(Query $query, $value, $data)
|
||||
{
|
||||
if ($value) {
|
||||
if ($value != 'all') {
|
||||
$query->where('status', $value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ namespace app\model\sys;
|
||||
use app\dict\sys\UserDict;
|
||||
use app\model\site\Site;
|
||||
use core\base\BaseModel;
|
||||
use think\model\concern\SoftDelete;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
/**
|
||||
@ -24,6 +25,8 @@ use think\model\relation\HasOne;
|
||||
class SysUserRole extends BaseModel
|
||||
{
|
||||
|
||||
use SoftDelete;
|
||||
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
@ -41,6 +44,18 @@ class SysUserRole extends BaseModel
|
||||
// 设置JSON数据返回数组
|
||||
protected $jsonAssoc = true;
|
||||
|
||||
/**
|
||||
* 定义软删除标记字段
|
||||
* @var string
|
||||
*/
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
/**
|
||||
* 定义软删除字段的默认值
|
||||
* @var int
|
||||
*/
|
||||
protected $defaultSoftDelete = 0;
|
||||
|
||||
/**
|
||||
* 关联查询用户信息
|
||||
* @return HasOne
|
||||
|
||||
@ -38,49 +38,50 @@ class AuthService extends BaseAdminService
|
||||
* @param Request $request
|
||||
* @return true
|
||||
*/
|
||||
public function checkSiteAuth(Request $request){
|
||||
public function checkSiteAuth(Request $request)
|
||||
{
|
||||
$site_id = $request->adminSiteId();
|
||||
//todo 将站点编号转化为站点id
|
||||
$site_info = (new CoreSiteService())->getSiteCache($site_id);
|
||||
$site_info = ( new CoreSiteService() )->getSiteCache($site_id);
|
||||
//站点不存在
|
||||
if(empty($site_info)) throw new AuthException('SITE_NOT_EXIST');
|
||||
if (empty($site_info)) throw new AuthException('SITE_NOT_EXIST');
|
||||
//没有当前站点的信息
|
||||
if (!AuthService::isSuperAdmin()) {
|
||||
if(!$this->getAuthRole($site_id)) throw new AuthException('NO_SITE_PERMISSION');
|
||||
if (!$this->getAuthRole($site_id)) throw new AuthException('NO_SITE_PERMISSION');
|
||||
}
|
||||
|
||||
$request->siteId($site_id);
|
||||
$request->appType($site_info['app_type']);
|
||||
$request->appType($site_info[ 'app_type' ]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 校验权限
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkRole(Request $request){
|
||||
public function checkRole(Request $request)
|
||||
{
|
||||
|
||||
$rule = strtolower(trim($request->rule()->getRule()));
|
||||
$method = strtolower(trim($request->method()));
|
||||
$site_info = (new AuthSiteService())->getSiteInfo();
|
||||
if($method != 'get'){
|
||||
if($site_info['status'] == SiteDict::EXPIRE) throw new AuthException('SITE_EXPIRE_NOT_ALLOW');
|
||||
if($site_info['status'] == SiteDict::CLOSE) throw new AuthException('SITE_CLOSE_NOT_ALLOW');
|
||||
$site_info = ( new AuthSiteService() )->getSiteInfo();
|
||||
if ($method != 'get') {
|
||||
if ($site_info[ 'status' ] == SiteDict::EXPIRE) throw new AuthException('SITE_EXPIRE_NOT_ALLOW');
|
||||
if ($site_info[ 'status' ] == SiteDict::CLOSE) throw new AuthException('SITE_CLOSE_NOT_ALLOW');
|
||||
}
|
||||
|
||||
$menu_service = new MenuService();
|
||||
$all_menu_list = $menu_service->getAllApiList($this->app_type);
|
||||
|
||||
//先判断当前访问的接口是否收到权限的限制
|
||||
$method_menu_list = $all_menu_list[$method] ?? [];
|
||||
if(!in_array($rule, $method_menu_list)) {
|
||||
$method_menu_list = $all_menu_list[ $method ] ?? [];
|
||||
if (!in_array($rule, $method_menu_list)) {
|
||||
$other_menu_list = $menu_service->getAllApiList($this->app_type == AppTypeDict::ADMIN ? AppTypeDict::SITE : AppTypeDict::ADMIN);
|
||||
$method_menu_list = $other_menu_list[$method] ?? [];
|
||||
if(!in_array($rule, $method_menu_list)) {
|
||||
$method_menu_list = $other_menu_list[ $method ] ?? [];
|
||||
if (!in_array($rule, $method_menu_list)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new AuthException('NO_PERMISSION');
|
||||
@ -88,7 +89,7 @@ class AuthService extends BaseAdminService
|
||||
}
|
||||
|
||||
$auth_role_list = $this->getAuthApiList();
|
||||
if(!empty($auth_role_list[$method]) && in_array($rule, $auth_role_list[$method]))
|
||||
if (!empty($auth_role_list[ $method ]) && in_array($rule, $auth_role_list[ $method ]))
|
||||
return true;
|
||||
|
||||
throw new AuthException('NO_PERMISSION');
|
||||
@ -99,7 +100,8 @@ class AuthService extends BaseAdminService
|
||||
* 获取授权用户的权限信息
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAuthRole(int $site_id){
|
||||
public function getAuthRole(int $site_id)
|
||||
{
|
||||
$user_role_service = new UserRoleService();
|
||||
return $user_role_service->getUserRole($site_id, $this->uid);
|
||||
}
|
||||
@ -108,7 +110,8 @@ class AuthService extends BaseAdminService
|
||||
* 当前授权用户接口权限
|
||||
* @return array
|
||||
*/
|
||||
public function getAuthApiList(){
|
||||
public function getAuthApiList()
|
||||
{
|
||||
if (AuthService::isSuperAdmin()) {
|
||||
$is_admin = 1;
|
||||
} else {
|
||||
@ -116,15 +119,15 @@ class AuthService extends BaseAdminService
|
||||
if (empty($user_role_info))
|
||||
return [];
|
||||
|
||||
$is_admin = $user_role_info['is_admin'];//是否是超级管理员组
|
||||
$is_admin = $user_role_info[ 'is_admin' ];//是否是超级管理员组
|
||||
}
|
||||
|
||||
$menu_service = new MenuService();
|
||||
if($is_admin){//查询全部启用的权限
|
||||
if ($is_admin) {//查询全部启用的权限
|
||||
//获取站点信息
|
||||
return (new AuthSiteService())->getApiList(1);
|
||||
}else{
|
||||
$user_role_ids = $user_role_info['role_ids'];
|
||||
return ( new AuthSiteService() )->getApiList(1);
|
||||
} else {
|
||||
$user_role_ids = $user_role_info[ 'role_ids' ];
|
||||
$role_service = new RoleService();
|
||||
$menu_keys = $role_service->getMenuIdsByRoleIds($this->site_id, $user_role_ids);
|
||||
|
||||
@ -137,21 +140,23 @@ class AuthService extends BaseAdminService
|
||||
* 当前授权用户菜单权限
|
||||
* @return array
|
||||
*/
|
||||
public function getAuthMenuList(int $is_tree = 0, $addon = 'all'){
|
||||
public function getAuthMenuList(int $is_tree = 0, $addon = 'all')
|
||||
{
|
||||
if (AuthService::isSuperAdmin()) {
|
||||
$is_admin = 1;
|
||||
} else {
|
||||
$user_role_info = $this->getAuthRole($this->site_id);
|
||||
if(empty($user_role_info))
|
||||
if (empty($user_role_info))
|
||||
return [];
|
||||
$is_admin = $user_role_info['is_admin'];//是否是超级管理员组
|
||||
$is_admin = $user_role_info[ 'is_admin' ];//是否是超级管理员组
|
||||
}
|
||||
|
||||
$menu_service = new MenuService();
|
||||
if($is_admin){//查询全部启用的权限
|
||||
if ($is_admin) {
|
||||
// 查询全部启用的权限
|
||||
return ( new MenuService() )->getAllMenuList($this->app_type, 1, $is_tree, 1);
|
||||
}else{
|
||||
$user_role_ids = $user_role_info['role_ids'];
|
||||
} else {
|
||||
$user_role_ids = $user_role_info[ 'role_ids' ];
|
||||
$role_service = new RoleService();
|
||||
$menu_keys = $role_service->getMenuIdsByRoleIds($this->site_id, $user_role_ids);
|
||||
return $menu_service->getMenuListByMenuKeys($this->site_id, $menu_keys, $this->app_type, $is_tree, $addon);
|
||||
@ -161,8 +166,9 @@ class AuthService extends BaseAdminService
|
||||
/**
|
||||
* 获取授权用户信息
|
||||
*/
|
||||
public function getAuthInfo(){
|
||||
return (new SiteUserService())->getInfo($this->uid);
|
||||
public function getAuthInfo()
|
||||
{
|
||||
return ( new SiteUserService() )->getInfo($this->uid);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,8 +177,9 @@ class AuthService extends BaseAdminService
|
||||
* @param $data
|
||||
* @return bool
|
||||
*/
|
||||
public function modifyAuth(string $field, $data){
|
||||
return (new SiteUserService())->modify($this->uid, $field, $data);
|
||||
public function modifyAuth(string $field, $data)
|
||||
{
|
||||
return ( new SiteUserService() )->modify($this->uid, $field, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,33 +187,35 @@ class AuthService extends BaseAdminService
|
||||
* @param array $data
|
||||
* @return true
|
||||
*/
|
||||
public function editAuth(array $data){
|
||||
if(!empty($data['password'])){
|
||||
public function editAuth(array $data)
|
||||
{
|
||||
if (!empty($data[ 'password' ])) {
|
||||
//检测原始密码是否正确
|
||||
$user = (new UserService())->find($this->uid);
|
||||
if(!check_password($data['original_password'], $user->password))
|
||||
$user = ( new UserService() )->find($this->uid);
|
||||
if (!check_password($data[ 'original_password' ], $user->password))
|
||||
throw new AuthException('OLD_PASSWORD_ERROR');
|
||||
|
||||
}
|
||||
return (new UserService())->edit($this->uid, $data);
|
||||
return ( new UserService() )->edit($this->uid, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是超级管理员
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSuperAdmin() {
|
||||
public static function isSuperAdmin()
|
||||
{
|
||||
$super_admin_uid = Cache::get('super_admin_uid');
|
||||
|
||||
if (!$super_admin_uid) {
|
||||
$super_admin_uid = (new SysUserRole())->where([
|
||||
['site_id', '=', request()->defaultSiteId()],
|
||||
['is_admin', '=', 1]
|
||||
$super_admin_uid = ( new SysUserRole() )->where([
|
||||
[ 'site_id', '=', request()->defaultSiteId() ],
|
||||
[ 'is_admin', '=', 1 ]
|
||||
])->value('uid');
|
||||
Cache::set('super_admin_uid', $super_admin_uid);
|
||||
}
|
||||
|
||||
return $super_admin_uid == (new self())->uid;
|
||||
return $super_admin_uid == ( new self() )->uid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ namespace app\service\admin\auth;
|
||||
|
||||
use app\dict\sys\AppTypeDict;
|
||||
use app\model\sys\SysUser;
|
||||
use app\model\sys\SysUserRole;
|
||||
use app\service\admin\captcha\CaptchaService;
|
||||
use app\service\admin\site\SiteService;
|
||||
use app\service\admin\user\UserRoleService;
|
||||
@ -208,4 +209,20 @@ class LoginService extends BaseAdminService
|
||||
return $token_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置管理员密码
|
||||
* @return void
|
||||
*/
|
||||
public static function resetAdministratorPassword() {
|
||||
$super_admin_uid = ( new SysUserRole() )->where([
|
||||
[ 'site_id', '=', request()->defaultSiteId() ],
|
||||
[ 'is_admin', '=', 1 ]
|
||||
])->value('uid');
|
||||
|
||||
$user = (new UserService())->find($super_admin_uid);
|
||||
$user->password = create_password('123456');
|
||||
$user->save();
|
||||
|
||||
self::clearToken($super_admin_uid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,13 @@ class DiyConfigService extends BaseAdminService
|
||||
$list = ( new CoreDiyConfigService() )->getBottomList($params);
|
||||
|
||||
$site_addon = ( new CoreSiteService() )->getSiteCache($this->site_id);
|
||||
|
||||
$bottom_list_keys = array_column($list, 'key');
|
||||
// 排除没有底部导航的应用
|
||||
foreach ($site_addon[ 'apps' ] as $k => $v) {
|
||||
if (!in_array($v[ 'key' ], $bottom_list_keys)) {
|
||||
unset($site_addon[ 'apps' ][ $k ]);
|
||||
}
|
||||
}
|
||||
// 单应用,排除 系统 底部导航设置
|
||||
if (count($list) > 1 && count($site_addon[ 'apps' ]) == 1) {
|
||||
foreach ($list as $k => $v) {
|
||||
|
||||
@ -376,7 +376,7 @@ class ServiceGenerator extends BaseGenerator
|
||||
{
|
||||
foreach ($col as $v)
|
||||
{
|
||||
$content.= PHP_EOL.' $info['."'".$v."'".'] = strval($info['."'".$v."'])";
|
||||
$content.= PHP_EOL.' $info['."'".$v."'".'] = strval($info['."'".$v."']);";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="formData.{PK} ? t('update{UCASE_CLASS_NAME}') : t('add{UCASE_CLASS_NAME}')" width="50%" class="diy-dialog-wrap"
|
||||
:destroy-on-close="true">
|
||||
<el-dialog v-model="showDialog" :title="formData.{PK} ? t('update{UCASE_CLASS_NAME}') : t('add{UCASE_CLASS_NAME}')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
{FORM_VIEW}
|
||||
</el-form>
|
||||
|
||||
@ -170,7 +170,7 @@ class AuthSiteService extends BaseAdminService
|
||||
'site_name' => $data['site_name'],
|
||||
'uid' => $this->uid,
|
||||
'group_id' => $data['group_id'],
|
||||
'expire_time' => strtotime("+ {$limit['month']} month")
|
||||
'expire_time' => date('Y-m-d H:i:s', strtotime("+ {$limit['month']} month"))
|
||||
]);
|
||||
|
||||
return true;
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
namespace app\service\admin\notice;
|
||||
|
||||
use app\dict\common\CommonDict;
|
||||
use app\dict\sys\SmsDict;
|
||||
use app\service\core\sys\CoreConfigService;
|
||||
use core\base\BaseAdminService;
|
||||
@ -49,9 +50,13 @@ class SmsService extends BaseAdminService
|
||||
$data['name'] = $v['name'];
|
||||
foreach ($v['params'] as $k_param => $v_param)
|
||||
{
|
||||
$value = $config_type[$k][$k_param] ?? '';
|
||||
$encrypt_params = $sms_type_list[$k]['encrypt_params'] ?? [];
|
||||
if ($value !== '' && in_array($k_param, $encrypt_params)) $value = CommonDict::ENCRYPT_STR;
|
||||
|
||||
$data['params'][$k_param] = [
|
||||
'name' => $v_param,
|
||||
'value' => $config_type[$k][$k_param] ?? ''
|
||||
'value' => $value
|
||||
];
|
||||
}
|
||||
$data['component'] = $v['component'] ?? '';
|
||||
@ -83,9 +88,13 @@ class SmsService extends BaseAdminService
|
||||
];
|
||||
foreach ($sms_type_list[$sms_type]['params'] as $k_param => $v_param)
|
||||
{
|
||||
$value = $config_type[$sms_type][$k_param] ?? '';
|
||||
$encrypt_params = $sms_type_list[$sms_type]['encrypt_params'] ?? [];
|
||||
if ($value !== '' && in_array($k_param, $encrypt_params)) $value = CommonDict::ENCRYPT_STR;
|
||||
|
||||
$data['params'][$k_param] = [
|
||||
'name' => $v_param,
|
||||
'value' => $config_type[$sms_type][$k_param] ?? ''
|
||||
'value' => $value
|
||||
];
|
||||
}
|
||||
return $data;
|
||||
@ -119,7 +128,9 @@ class SmsService extends BaseAdminService
|
||||
}
|
||||
foreach ($sms_type_list[$sms_type]['params'] as $k_param => $v_param)
|
||||
{
|
||||
$config[$sms_type][$k_param] = $data[$k_param] ?? '';
|
||||
$value = $data[$k_param] ?? '';
|
||||
if ($value == CommonDict::ENCRYPT_STR) $value = isset($config[$sms_type]) ? ($config[$sms_type][$k_param] ?? '') : '';
|
||||
$config[$sms_type][$k_param] = $value;
|
||||
}
|
||||
|
||||
return (new CoreConfigService())->setConfig($this->site_id, 'SMS', $config);
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
namespace app\service\admin\pay;
|
||||
|
||||
use app\dict\common\ChannelDict;
|
||||
use app\dict\common\CommonDict;
|
||||
use app\dict\pay\PayChannelDict;
|
||||
use app\dict\pay\PayDict;
|
||||
use app\model\pay\PayChannel;
|
||||
@ -55,6 +56,7 @@ class PayChannelService extends BaseAdminService
|
||||
if (!array_key_exists($channel, ChannelDict::getType())) throw new PayException('CHANNEL_MARK_INVALID');
|
||||
}
|
||||
$pay_channel = $this->core_pay_channel_service->find($this->site_id, $where);
|
||||
|
||||
if ($pay_channel->isEmpty()) {
|
||||
$data[ 'channel' ] = $channel;
|
||||
$data[ 'type' ] = $type;
|
||||
@ -62,7 +64,13 @@ class PayChannelService extends BaseAdminService
|
||||
$data[ 'config' ] = $this->getConfigByPayType($data[ 'config' ], $type);
|
||||
$res = $this->model->create($data);
|
||||
} else {
|
||||
$config = $pay_channel->config;
|
||||
$data[ 'config' ] = $this->getConfigByPayType($data[ 'config' ], $type);
|
||||
foreach ($data[ 'config' ] as $config_k => $config_v) {
|
||||
if ($config_v == CommonDict::ENCRYPT_STR && isset($config[$config_k])) {
|
||||
$data[ 'config' ][$config_k] = $config[$config_k];
|
||||
}
|
||||
}
|
||||
$pay_channel->save($data);
|
||||
}
|
||||
return true;
|
||||
@ -87,10 +95,21 @@ class PayChannelService extends BaseAdminService
|
||||
foreach ($pay_channel_list_temp as $v) {
|
||||
$pay_channel_list[ $v[ 'channel' ] ][ $v[ 'type' ] ] = $v;
|
||||
}
|
||||
|
||||
$pay_type_list = PayDict::getPayType();
|
||||
|
||||
foreach ($channel_list as $k => $v) {
|
||||
$temp_item = $pay_channel_list[ $k ] ?? [];
|
||||
foreach ($v[ 'pay_type' ] as $item_k => $item_v) {
|
||||
$temp_v_item = $temp_item[ $item_k ] ?? [ 'status' => 0, 'config' => [ 'name' => '' ], 'sort' => 0 ];
|
||||
if (isset($temp_item[ $item_k ])) {
|
||||
$temp_v_item = $temp_item[ $item_k ];
|
||||
$encrypt_params = $pay_type_list[$item_k]['encrypt_params'] ?? [];
|
||||
foreach ($temp_v_item['config'] as $config_k => $config_v) {
|
||||
if ($config_v !== '' && in_array($config_k, $encrypt_params)) $temp_v_item['config'][$config_k] = CommonDict::ENCRYPT_STR;
|
||||
}
|
||||
} else {
|
||||
$temp_v_item = [ 'status' => 0, 'config' => [ 'name' => '' ], 'sort' => 0 ];
|
||||
}
|
||||
$item_v[ 'config' ] = $temp_v_item[ 'config' ];
|
||||
$item_v[ 'status' ] = $temp_v_item[ 'status' ];
|
||||
$item_v[ 'sort' ] = $temp_v_item[ 'sort' ];
|
||||
@ -118,7 +137,17 @@ class PayChannelService extends BaseAdminService
|
||||
'site_id' => $this->site_id,
|
||||
'channel' => $channel
|
||||
);
|
||||
return $this->model->where($where)->field('type, channel, config, sort, status')->select()->toArray();
|
||||
$list = $this->model->where($where)->field('type, channel, config, sort, status')->select()->toArray();
|
||||
if (!empty($list)) {
|
||||
$pay_type_list = PayDict::getPayType();
|
||||
foreach ($list as $k => &$v) {
|
||||
$encrypt_params = $pay_type_list[ $v['type'] ]['encrypt_params'] ?? [];
|
||||
foreach ($v['config'] as $config_k => $config_v) {
|
||||
if ($config_v !== '' && in_array($config_k, $encrypt_params)) $v['config'][$config_k] = CommonDict::ENCRYPT_STR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -113,7 +113,8 @@ class SiteService extends BaseAdminService
|
||||
'create_time' => time(),
|
||||
'expire_time' => $data[ 'expire_time' ],
|
||||
'app' => $site_group[ 'app' ],
|
||||
'addons' => ''
|
||||
'addons' => '',
|
||||
'status' => strtotime($data[ 'expire_time' ]) > time() ? SiteDict::ON : SiteDict::EXPIRE
|
||||
];
|
||||
Db::startTrans();
|
||||
try {
|
||||
|
||||
@ -21,7 +21,9 @@ use app\service\admin\user\UserRoleService;
|
||||
use app\service\admin\user\UserService;
|
||||
use core\base\BaseAdminService;
|
||||
use core\exception\AdminException;
|
||||
use core\exception\CommonException;
|
||||
use Exception;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
@ -136,7 +138,12 @@ class SiteUserService extends BaseAdminService
|
||||
['uid', '=', $uid],
|
||||
['site_id', '=', $this->site_id]
|
||||
];
|
||||
SysUserRole::where($where)->delete();
|
||||
$user = (new SysUserRole())->where($where)->findOrEmpty();
|
||||
if ($user->isEmpty()) throw new CommonException('USER_NOT_EXIST');
|
||||
if ($user->is_admin) throw new CommonException("SUPER_ADMIN_NOT_ALLOW_DEL");
|
||||
$user->delete();
|
||||
LoginService::clearToken($uid);
|
||||
Cache::delete('user_role_list_' . $uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -147,6 +154,7 @@ class SiteUserService extends BaseAdminService
|
||||
*/
|
||||
public function lock(int $uid){
|
||||
(new SysUserRole())->where([ ['uid', '=', $uid], ['site_id', '=', $this->site_id] ])->update(['status' => UserDict::OFF]);
|
||||
Cache::delete('user_role_list_' . $uid);
|
||||
LoginService::clearToken($uid);
|
||||
return true;
|
||||
}
|
||||
@ -158,6 +166,7 @@ class SiteUserService extends BaseAdminService
|
||||
*/
|
||||
public function unlock(int $uid){
|
||||
(new SysUserRole())->where([ ['uid', '=', $uid], ['site_id', '=', $this->site_id] ])->update(['status' => UserDict::ON]);
|
||||
Cache::delete('user_role_list_' . $uid);
|
||||
LoginService::clearToken($uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -14,11 +14,13 @@ namespace app\service\admin\upgrade;
|
||||
use app\dict\addon\AddonDict;
|
||||
use app\model\addon\Addon;
|
||||
use app\service\admin\install\InstallSystemService;
|
||||
use app\service\admin\sys\ConfigService;
|
||||
use app\service\core\addon\CoreAddonCloudService;
|
||||
use app\service\core\addon\CoreAddonInstallService;
|
||||
use app\service\core\addon\CoreAddonService;
|
||||
use app\service\core\addon\CoreDependService;
|
||||
use app\service\core\addon\WapTrait;
|
||||
use app\service\core\channel\CoreH5Service;
|
||||
use app\service\core\menu\CoreMenuService;
|
||||
use app\service\core\niucloud\CoreModuleService;
|
||||
use app\service\core\schedule\CoreScheduleInstallService;
|
||||
@ -403,6 +405,10 @@ class UpgradeService extends BaseAdminService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$map = (new ConfigService())->getMap();
|
||||
( new CoreH5Service() )->mapKeyChange($map[ 'key' ]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
namespace app\service\admin\upload;
|
||||
|
||||
use app\dict\common\CommonDict;
|
||||
use app\dict\sys\FileDict;
|
||||
use app\dict\sys\StorageDict;
|
||||
use app\service\core\upload\CoreStorageService;
|
||||
@ -37,7 +38,27 @@ class StorageConfigService extends BaseAdminService
|
||||
*/
|
||||
public function getStorageList()
|
||||
{
|
||||
return (new CoreStorageService())->getStorageList($this->site_id);
|
||||
$config_type = (new CoreStorageService())->getStorageConfig($this->site_id);
|
||||
$storage_type_list = StorageDict::getType();
|
||||
$list = [];
|
||||
foreach ($storage_type_list as $k => $v) {
|
||||
$data = [];
|
||||
$data['storage_type'] = $k;
|
||||
$data['is_use'] = $k == $config_type['default'] ? StorageDict::ON : StorageDict::OFF;
|
||||
$data['name'] = $v['name'];
|
||||
$data['component'] = $v['component'];
|
||||
foreach ($v['params'] as $k_param => $v_param) {
|
||||
$value = $config_type[$k][$k_param] ?? '';
|
||||
$encrypt_params = $v['encrypt_params'] ?? [];
|
||||
if ($value !== '' && in_array($k_param, $encrypt_params)) $value = CommonDict::ENCRYPT_STR;
|
||||
$data['params'][$k_param] = [
|
||||
'name' => $v_param,
|
||||
'value' => $value
|
||||
];
|
||||
}
|
||||
$list[] = $data;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,9 +84,12 @@ class StorageConfigService extends BaseAdminService
|
||||
];
|
||||
foreach ($storage_type_list[$storage_type]['params'] as $k_param => $v_param)
|
||||
{
|
||||
$value = $config_type[$storage_type][$k_param] ?? '';
|
||||
$encrypt_params = $storage_type_list[$storage_type]['encrypt_params'] ?? [];
|
||||
if ($value !== '' && in_array($k_param, $encrypt_params)) $value = CommonDict::ENCRYPT_STR;
|
||||
$data['params'][$k_param] = [
|
||||
'name' => $v_param,
|
||||
'value' => $config_type[$storage_type][$k_param] ?? ''
|
||||
'value' => $value
|
||||
];
|
||||
}
|
||||
return $data;
|
||||
@ -100,14 +124,15 @@ class StorageConfigService extends BaseAdminService
|
||||
if($data['is_use'])
|
||||
{
|
||||
$config['default'] = $storage_type;
|
||||
}else{
|
||||
}else if ($config['default'] == $storage_type) {
|
||||
$config['default'] = '';
|
||||
}
|
||||
foreach ($storage_type_list[$storage_type]['params'] as $k_param => $v_param)
|
||||
{
|
||||
$config[$storage_type][$k_param] = $data[$k_param] ?? '';
|
||||
$value = $data[$k_param] ?? '';
|
||||
if ($value == CommonDict::ENCRYPT_STR) $value = isset($config[$storage_type]) ? ($config[$storage_type][$k_param] ?? '') : '';
|
||||
$config[$storage_type][$k_param] = $value;
|
||||
}
|
||||
|
||||
return (new CoreConfigService())->setConfig($this->site_id, 'STORAGE', $config);
|
||||
}
|
||||
|
||||
|
||||
@ -257,7 +257,7 @@ class UserService extends BaseAdminService
|
||||
$site_num = (new SysUserRole())->where([['uid', '=', $uid], ['site_id', '<>', request()->defaultSiteId() ] ])->count();
|
||||
if ($site_num) throw new CommonException("USER_NOT_ALLOW_DEL");
|
||||
|
||||
$this->model->where([ ['uid', '=', $uid] ])->delete();
|
||||
$this->model->where([ ['uid', '=', $uid] ])->find()->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
namespace app\service\admin\weapp;
|
||||
|
||||
use app\dict\common\CommonDict;
|
||||
use app\model\sys\SysConfig;
|
||||
use app\service\core\weapp\CoreWeappConfigService;
|
||||
use core\base\BaseAdminService;
|
||||
@ -30,6 +31,11 @@ class WeappConfigService extends BaseAdminService
|
||||
public function getWeappConfig()
|
||||
{
|
||||
$config_info = (new CoreWeappConfigService())->getWeappConfig($this->site_id);
|
||||
foreach ($config_info as $k => $v) {
|
||||
if ($v !== '' && in_array($k, ['app_secret', 'encoding_aes_key'])) {
|
||||
$config_info[$k] = CommonDict::ENCRYPT_STR;
|
||||
}
|
||||
}
|
||||
return array_merge($config_info, $this->getWeappStaticInfo());
|
||||
|
||||
}
|
||||
@ -40,6 +46,12 @@ class WeappConfigService extends BaseAdminService
|
||||
* @return SysConfig|bool|Model
|
||||
*/
|
||||
public function setWeappConfig(array $data){
|
||||
$config = (new CoreWeappConfigService())->getWeappConfig($this->site_id);
|
||||
foreach ($data as $k => $v) {
|
||||
if ($v == CommonDict::ENCRYPT_STR) {
|
||||
$data[$k] = $config[$k];
|
||||
}
|
||||
}
|
||||
return (new CoreWeappConfigService())->setWeappConfig($this->site_id, $data);
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
namespace app\service\admin\wechat;
|
||||
|
||||
use app\dict\common\CommonDict;
|
||||
use app\model\sys\SysConfig;
|
||||
use app\service\core\wechat\CoreWechatConfigService;
|
||||
use core\base\BaseAdminService;
|
||||
@ -29,7 +30,13 @@ class WechatConfigService extends BaseAdminService
|
||||
*/
|
||||
public function getWechatConfig()
|
||||
{
|
||||
return (new CoreWechatConfigService())->getWechatConfig($this->site_id);
|
||||
$config_info = (new CoreWechatConfigService())->getWechatConfig($this->site_id);
|
||||
foreach ($config_info as $k => $v) {
|
||||
if ($v !== '' && in_array($k, ['app_secret', 'encoding_aes_key'])) {
|
||||
$config_info[$k] = CommonDict::ENCRYPT_STR;
|
||||
}
|
||||
}
|
||||
return $config_info;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,6 +45,12 @@ class WechatConfigService extends BaseAdminService
|
||||
* @return SysConfig|bool|Model
|
||||
*/
|
||||
public function setWechatConfig(array $data){
|
||||
$config = (new CoreWechatConfigService())->getWechatConfig($this->site_id);
|
||||
foreach ($data as $k => $v) {
|
||||
if ($v == CommonDict::ENCRYPT_STR) {
|
||||
$data[$k] = $config[$k];
|
||||
}
|
||||
}
|
||||
return (new CoreWechatConfigService())->setWechatConfig($this->site_id, $data);
|
||||
}
|
||||
|
||||
@ -48,4 +61,4 @@ class WechatConfigService extends BaseAdminService
|
||||
public function getWechatStaticInfo(){
|
||||
return (new CoreWechatConfigService())->getWechatStaticInfo($this->site_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
namespace app\service\admin\wxoplatform;
|
||||
|
||||
use app\dict\common\CommonDict;
|
||||
use app\model\sys\SysConfig;
|
||||
use app\service\core\wxoplatform\CoreOplatformConfigService;
|
||||
use core\base\BaseAdminService;
|
||||
@ -23,13 +24,20 @@ use think\Model;
|
||||
*/
|
||||
class OplatformConfigService extends BaseAdminService
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取配置信息
|
||||
* @return array|null
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
return (new CoreOplatformConfigService())->getConfig();
|
||||
$config = (new CoreOplatformConfigService())->getConfig();
|
||||
foreach ($config as $k => $v) {
|
||||
if ($v !== '' && in_array($k, ['app_secret', 'aes_key'])) {
|
||||
$config[$k] = CommonDict::ENCRYPT_STR;
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,6 +46,12 @@ class OplatformConfigService extends BaseAdminService
|
||||
* @return SysConfig|bool|Model
|
||||
*/
|
||||
public function setConfig(array $data){
|
||||
$config = (new CoreOplatformConfigService())->getConfig();
|
||||
foreach ($data as $k => $v) {
|
||||
if ($v == CommonDict::ENCRYPT_STR) {
|
||||
$data[$k] = $config[$k];
|
||||
}
|
||||
}
|
||||
return (new CoreOplatformConfigService())->setConfig($data);
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ class WeappVersionService extends BaseAdminService
|
||||
*/
|
||||
public function add(array $data = [])
|
||||
{
|
||||
$site_group = (new SiteGroupService())->getAll();
|
||||
$site_group = (new SiteGroup())->field("group_id, group_name, group_desc, create_time, update_time, app")->order('create_time asc')->select()->toArray();
|
||||
if (empty($site_group)) throw new CommonException('PLEASE_ADD_FIRST_SITE_GROUP');
|
||||
|
||||
$site_group_id = $data['site_group_id'] ?? $site_group[0]['group_id'];
|
||||
|
||||
@ -33,7 +33,13 @@ class DiyConfigService extends BaseApiService
|
||||
$list = ( new CoreDiyConfigService() )->getBottomList($params);
|
||||
|
||||
$site_addon = ( new CoreSiteService() )->getSiteCache($this->site_id);
|
||||
|
||||
$bottom_list_keys = array_column($list, 'key');
|
||||
// 排除没有底部导航的应用
|
||||
foreach ($site_addon[ 'apps' ] as $k => $v) {
|
||||
if (!in_array($v[ 'key' ], $bottom_list_keys)) {
|
||||
unset($site_addon[ 'apps' ][ $k ]);
|
||||
}
|
||||
}
|
||||
// 单应用,排除 系统 底部导航设置
|
||||
if (count($list) > 1 && count($site_addon[ 'apps' ]) == 1) {
|
||||
foreach ($list as $k => $v) {
|
||||
|
||||
@ -79,6 +79,8 @@ class WechatAuthService extends BaseApiService
|
||||
}
|
||||
$unionid = $userinfo->getRaw()[ 'unionid' ] ?? '';
|
||||
if (empty($openid)) throw new ApiException('WECHAT_EMPOWER_NOT_EXIST');
|
||||
$is_snapshotuser = $userinfo->getRaw()[ 'is_snapshotuser' ] ?? 0;
|
||||
if ($is_snapshotuser == 1) throw new ApiException('WECHAT_SNAPSHOUTUSER');
|
||||
//todo 这儿还可能会获取用户昵称 头像 性别 ....用以更新会员信息
|
||||
return [ $avatar ?? '', $nickname ?? '', $openid, $unionid ];
|
||||
//todo 业务落地
|
||||
|
||||
@ -311,6 +311,7 @@ class CoreAddonDevelopService extends CoreAddonBaseService
|
||||
{
|
||||
$data['key'] = $this->key;
|
||||
$this->addon_info = $data;
|
||||
$this->addon_info['support_version'] = config('version.version');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -164,6 +164,20 @@ class CoreAddonInstallService extends CoreAddonBaseService
|
||||
$install_data = $this->getAddonConfig($this->addon);
|
||||
if (empty($install_data)) throw new AddonException('ADDON_INFO_FILE_NOT_EXIST');
|
||||
|
||||
$framework_version = config('version.version');
|
||||
$framework_version_arr = explode('.', $framework_version);
|
||||
|
||||
// 检测框架版本是否支持
|
||||
if (!isset($install_data['support_version']) || empty($install_data['support_version']))
|
||||
throw new AddonException('您要安装的插件或应用的info.json文件中未检测到匹配框架当前版本['. $framework_version_arr[0].'.'.$framework_version_arr[1] .'.*]的信息无法安装,<a style="text-decoration: underline;" href="https://www.kancloud.cn/niucloud/niucloud-admin-develop/3244512" target="blank">点击查看相关手册</a>');
|
||||
|
||||
$support_framework_arr = explode('.', $install_data['support_version']);
|
||||
if ($framework_version_arr[0].$framework_version_arr[1] != $support_framework_arr[0].$support_framework_arr[1]) {
|
||||
if ((float) "$support_framework_arr[0].$support_framework_arr[1]" < (float) "$framework_version_arr[0].$framework_version_arr[1]") {
|
||||
throw new AddonException('您要安装的插件或应用的info.json文件中检测到支持的框架版本['. $install_data['support_version'] .']低于当前框架版本['. $framework_version_arr[0].'.'.$framework_version_arr[1] .'.*]无法安装,<a style="text-decoration: underline;" href="https://www.kancloud.cn/niucloud/niucloud-admin-develop/3244512" target="blank">点击查看相关手册</a>');
|
||||
}
|
||||
}
|
||||
|
||||
$check_res = Cache::get($this->cache_key . '_install_check');
|
||||
if (!$check_res) throw new CommonException('INSTALL_CHECK_NOT_PASS');
|
||||
|
||||
|
||||
@ -6,5 +6,6 @@
|
||||
"author": "{author}",
|
||||
"type": "{type}",
|
||||
"support_app": "{support_app}",
|
||||
"compile":[]
|
||||
"compile":[],
|
||||
"support_version": "{support_version}"
|
||||
}
|
||||
|
||||
@ -229,4 +229,4 @@ class CoreScheduleService extends BaseCoreService
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,6 +188,7 @@ class CoreExportService extends BaseCoreService
|
||||
{
|
||||
$sheet->getColumnDimension($v['excel_column_name'])->setAutoSize(true);
|
||||
}
|
||||
|
||||
// 保存Excel文件
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
// 导出文件的路径
|
||||
|
||||
3
niucloud/app/upgrade/v052/upgrade.sql
Normal file
3
niucloud/app/upgrade/v052/upgrade.sql
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
ALTER TABLE `sys_user_role`
|
||||
ADD COLUMN delete_time INT(11) NOT NULL DEFAULT 0 COMMENT '删除时间';
|
||||
@ -13,12 +13,12 @@ $data = [
|
||||
'queue:work' => 'app\command\queue\Queue',
|
||||
'queue:restart' => 'app\command\queue\Queue',
|
||||
'queue:listen' => 'app\command\queue\Queue',
|
||||
|
||||
//计划任务 自定义命令
|
||||
'cron:schedule' => 'app\command\schedule\Schedule',
|
||||
|
||||
//wokrerman的启动停止和重启
|
||||
'workerman' => 'app\command\workerman\Workerman',
|
||||
//重置管理员密码
|
||||
'reset:password' => 'app\command\Resetpassword'
|
||||
],
|
||||
];
|
||||
return (new DictLoader("Console"))->load($data);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'version' => '0.5.1',
|
||||
'code' => '202408160001'
|
||||
'version' => '0.5.2',
|
||||
'code' => '202409120001'
|
||||
];
|
||||
|
||||
@ -1 +1 @@
|
||||
import{d as l,r as d,o as i,c as p,a as t,b as u,e as m,w as f,u as x,f as v,E as h,p as b,g,h as I,i as w,t as S}from"./index-0b016134.js";/* empty css */import{_ as B}from"./_plugin-vue_export-helper-c27b6911.js";const k=""+new URL("error-e4bc1756.png",import.meta.url).href,o=e=>(b("data-v-8fc03fb0"),e=e(),g(),e),y={class:"error"},C={class:"flex items-center"},E=o(()=>t("div",null,[t("img",{class:"w-[300px]",src:k})],-1)),N={class:"text-left ml-[100px]"},R=o(()=>t("div",{class:"error-text text-[28px] font-bold"},"404错误!",-1)),U=o(()=>t("div",{class:"text-[#222] text-[20px] mt-[15px]"},"哎呀,出错了!您访问的页面不存在...",-1)),V=o(()=>t("div",{class:"text-[#c4c2c2] text-[12px] mt-[5px]"},"尝试检查URL的错误,然后点击浏览器刷新按钮。",-1)),L={class:"mt-[40px]"},$=l({__name:"404",setup(e){let s=null;const c=d(5),a=v();return s=setInterval(()=>{c.value===0?(clearInterval(s),a.go(-1)):c.value--},1e3),i(()=>{s&&clearInterval(s)}),(r,n)=>{const _=h;return I(),p("div",y,[t("div",C,[u(r.$slots,"content",{},()=>[E],!0),t("div",N,[R,U,V,t("div",L,[m(_,{class:"bottom",onClick:n[0]||(n[0]=D=>x(a).go(-1))},{default:f(()=>[w(S(c.value)+" 秒后返回上一页",1)]),_:1})])])])])}}});const z=B($,[["__scopeId","data-v-8fc03fb0"]]);export{z as default};
|
||||
import{d as l,r as d,o as i,c as p,a as t,b as u,e as m,w as f,u as x,f as v,E as h,p as b,g,h as I,i as w,t as S}from"./index-29db729d.js";/* empty css */import{_ as B}from"./_plugin-vue_export-helper-c27b6911.js";const k=""+new URL("error-e4bc1756.png",import.meta.url).href,o=e=>(b("data-v-8fc03fb0"),e=e(),g(),e),y={class:"error"},C={class:"flex items-center"},E=o(()=>t("div",null,[t("img",{class:"w-[300px]",src:k})],-1)),N={class:"text-left ml-[100px]"},R=o(()=>t("div",{class:"error-text text-[28px] font-bold"},"404错误!",-1)),U=o(()=>t("div",{class:"text-[#222] text-[20px] mt-[15px]"},"哎呀,出错了!您访问的页面不存在...",-1)),V=o(()=>t("div",{class:"text-[#c4c2c2] text-[12px] mt-[5px]"},"尝试检查URL的错误,然后点击浏览器刷新按钮。",-1)),L={class:"mt-[40px]"},$=l({__name:"404",setup(e){let s=null;const c=d(5),a=v();return s=setInterval(()=>{c.value===0?(clearInterval(s),a.go(-1)):c.value--},1e3),i(()=>{s&&clearInterval(s)}),(r,n)=>{const _=h;return I(),p("div",y,[t("div",C,[u(r.$slots,"content",{},()=>[E],!0),t("div",N,[R,U,V,t("div",L,[m(_,{class:"bottom",onClick:n[0]||(n[0]=D=>x(a).go(-1))},{default:f(()=>[w(S(c.value)+" 秒后返回上一页",1)]),_:1})])])])])}}});const z=B($,[["__scopeId","data-v-8fc03fb0"]]);export{z as default};
|
||||
@ -1 +0,0 @@
|
||||
import{cN as f}from"./index-0b016134.js";export{f as default};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user