初始化admin pc端

This commit is contained in:
Mrtangl
2022-04-08 10:42:44 +08:00
parent d9c9f27530
commit 19665b64fb
137 changed files with 11991 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 KiB

View File

@@ -0,0 +1,183 @@
<template>
<div class="login flex flex-col">
<div class="flex-1 flex flex-center">
<div class="login-card bg-white flex">
<div
class="login-img"
:style="{
'background-image': `url(${config.login_image})`
}"
></div>
<div class="login-form flex flex-col">
<div class="f-s-24 f-w-500 text-center m-b-40">
{{ config.web_name }}
</div>
<el-form ref="loginFormRefs" :model="loginForm" status-icon :rules="rules">
<el-form-item prop="account">
<el-input
v-model="loginForm.account"
placeholder="请输入账号"
@keyup.enter="handleEnter"
>
<template #prepend>
<el-icon><avatar /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
ref="passwordRefs"
v-model="loginForm.password"
show-password
placeholder="请输入密码"
@keyup.enter="handleLogin"
>
<template #prepend>
<el-icon><lock /></el-icon>
</template>
</el-input>
</el-form-item>
</el-form>
<div class="m-b-20">
<el-checkbox v-model="remAccount" label="记住账号"></el-checkbox>
</div>
<el-button type="primary" :loading="loginLoading" @click="handleLogin"
>登录</el-button
>
</div>
</div>
</div>
<div class="login-footer">
<div class="flex flex-center muted xs m-t-20">
<span class="m-r-10">{{ config.copyright_info }}</span>
<a class="link muted" :href="config.icp_link" target="_blank">{{
config.icp_number
}}</a>
</div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, reactive, Ref, ref } from 'vue'
import { useAdmin } from '@/core/hooks/app'
import { ACCOUNT } from '@/config/cachekey'
import cache from '@/utils/cache'
import { ElInput, ElForm } from 'element-plus'
export default defineComponent({
setup() {
const { store, router, route } = useAdmin()
const passwordRefs: Ref<typeof ElInput | null> = ref(null)
const loginFormRefs: Ref<typeof ElForm | null> = ref(null)
const remAccount = ref(false)
const loginLoading = ref(false)
const config = computed(() => store.getters.config)
const loginForm = reactive({
account: '',
password: ''
})
const rules = {
account: [
{
required: true,
message: '请输入账号',
trigger: ['blur']
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['blur']
}
]
}
const handleEnter = () => {
if (!loginForm.password) {
return passwordRefs.value?.focus()
}
handleLogin()
}
const handleLogin = () => {
loginFormRefs.value?.validate((valid: boolean) => {
if (!valid) {
return
}
loginLoading.value = true
// 记住账号,缓存
cache.set(ACCOUNT, {
remember: remAccount.value,
account: loginForm.account
})
store
.dispatch('user/login', loginForm)
.then(() => {
const {
query: { redirect }
} = route
const path = typeof redirect === 'string' ? redirect : '/'
router.replace(path)
})
.catch(err => {
console.log(err)
})
.finally(() => {
loginLoading.value = false
})
})
}
onMounted(() => {
const value = cache.get(ACCOUNT)
if (value.remember) {
remAccount.value = value.remember
loginForm.account = value.account
}
})
return {
config,
passwordRefs,
loginFormRefs,
loginForm,
loginLoading,
rules,
handleEnter,
handleLogin,
remAccount
}
}
})
</script>
<style lang="scss" scoped>
.login {
min-height: 100vh;
background-image: url('./images/login_bg.png');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
.login-card {
width: 800px;
height: 400px;
border-radius: 10px;
overflow: hidden;
.login-img {
height: 100%;
width: 50%;
box-sizing: border-box;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.login-form {
width: 50%;
box-sizing: border-box;
padding: 30px 40px 0;
}
}
.login-footer {
padding: 20px 0;
}
}
</style>

View File

@@ -0,0 +1,365 @@
<template>
<!-- Header Start -->
<el-card shadow="never">
<el-page-header @back="$router.back()" content="编辑消息通知" />
</el-card>
<!-- Header End -->
<!-- Header Start -->
<el-card shadow="never" class="m-t-15">
<div class="xxl m-b-20">通知名称</div>
<div class="m-l-30 m-t-10">通知名称 {{ formData.scene_name }}</div>
<div class="m-l-30 m-t-10">通知类型 业务通知</div>
<div class="m-l-30 m-t-10">通知业务 {{ formData.scene_desc }}</div>
</el-card>
<!-- Header End -->
<!-- 系统通知 Start -->
<el-card shadow="never" class="m-t-15" v-if="formData.system_notice.is_show">
<div class="xxl m-b-20">系统通知</div>
<div class style="width: 90%">
<el-form ref="form" label-width="135px">
<el-form-item label="开启状态" required>
<el-radio v-model="formData.system_notice.status" :label="'0'">关闭</el-radio>
<el-radio v-model="formData.system_notice.status" :label="'1'">开启</el-radio>
</el-form-item>
<el-form-item label="通知标题" size="mini" required>
<el-input v-model="formData.system_notice.title"></el-input>
</el-form-item>
<el-form-item label="通知内容" size="mini" required>
<el-input
type="textarea"
class="text"
style="width: 300px"
placeholder="请输入内容"
v-model="formData.system_notice.content"
></el-input>
</el-form-item>
</el-form>
<div class="desc m-t-20" style="margin-left: 135px">
<div v-for="(item, index) in formData.system_notice.tips" :key="index">
{{ item }}
</div>
</div>
</div>
</el-card>
<!-- 系统通知 End -->
<!-- 短信通知 Start -->
<el-card shadow="never" class="m-t-15" v-if="formData.sms_notice.is_show">
<div class="xxl m-b-20">短信通知</div>
<div style="width: 90%">
<el-form ref="form" label-width="135px">
<el-form-item label="开启状态" required>
<el-radio v-model="formData.sms_notice.status" :label="'0'">关闭</el-radio>
<el-radio v-model="formData.sms_notice.status" :label="'1'">开启</el-radio>
</el-form-item>
<el-form-item label="模板ID" size="mini" required>
<el-input v-model="formData.sms_notice.template_id"></el-input>
</el-form-item>
<el-form-item label="短信内容" size="mini" required>
<el-input
type="textarea"
class="text"
style="width: 300px"
placeholder="请输入内容"
v-model="formData.sms_notice.content"
></el-input>
</el-form-item>
</el-form>
<div class="desc m-t-20" style="margin-left: 135px">
<div v-for="(item, index) in formData.sms_notice.tips" :key="index">{{ item }}</div>
</div>
</div>
</el-card>
<!-- 短信通知 End -->
<!-- 微信模板消息 Start -->
<el-card shadow="never" class="m-t-15" v-if="formData.oa_notice.is_show">
<div class="xxl m-b-20">微信模板消息</div>
<el-alert
title="温馨提示:
1. 请前往微信公众平台将【主营行业IT科技/互联网|电子商务 副营行业:消费品】类目添加至您的服务类目,否则将影响订阅通知的正常发送。
2. 查找订阅通知并选用调整关键词的顺序后复制模板ID粘贴在此页面对应的模板ID输入框中"
type="success"
show-icon
:closable="false"
/>
<div class style="width: 90%">
<el-form ref="form" label-width="135px">
<el-form-item label="开启状态" required>
<el-radio v-model="formData.oa_notice.status" :label="'0'">关闭</el-radio>
<el-radio v-model="formData.oa_notice.status" :label="'1'">开启</el-radio>
</el-form-item>
<el-form-item label="模板ID" size="mini" required>
<el-input v-model="formData.oa_notice.template_id"></el-input>
</el-form-item>
<el-form-item label="模板字段first内容" size="mini" required>
<el-input v-model="formData.oa_notice.first"></el-input>
</el-form-item>
<el-form-item label="模板字段remrk内容" size="mini" required>
<el-input v-model="formData.oa_notice.remark"></el-input>
</el-form-item>
<el-form-item label="模板内容" size="mini" required>
<el-button type="primary" size="mini" @click="onAddModeField"
>新增模板字段</el-button
>
<el-table
class="m-t-12"
:data="formData.oa_notice.tpl"
style="width: 100%"
size="mini"
>
<el-table-column label="字段名" width="120">
<template #="scope">
<el-input
v-model="formData.oa_notice.tpl[scope.$index].tpl_name"
placeholder="例如:订单编号"
></el-input>
</template>
</el-table-column>
<el-table-column label="字段值" width="120">
<template #="scope">
<el-input
v-model="formData.oa_notice.tpl[scope.$index].tpl_keyword"
placeholder="例如:keyword1.DT"
></el-input>
</template>
</el-table-column>
<el-table-column label="字段内容" width="180">
<template #="scope">
<el-input
v-model="formData.oa_notice.tpl[scope.$index].tpl_content"
placeholder="例如:${order.sn}"
></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #="scope">
<el-button
type="text"
size="mall"
@click="formData.oa_notice.tpl.splice(scope.$index, 1)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div class="desc m-t-20" style="margin-left: 135px">
<div v-for="(item, index) in formData.oa_notice.tips" :key="index">{{ item }}</div>
</div>
</div>
</el-card>
<!-- 微信模板消息 End -->
<!-- 微信小程序提醒 Start -->
<el-card shadow="never" class="m-t-15" v-if="formData.mnp_notice.is_show">
<div class="xxl m-b-20">微信小程序提醒</div>
<el-alert
title="温馨提示:
1. 请前往微信公众平台将【主营行业IT科技/互联网|电子商务 副营行业:消费品】类目添加至您的服务类目,否则将影响订阅通知的正常发送。
2. 查找订阅通知并选用调整关键词的顺序后复制模板ID粘贴在此页面对应的模板ID输入框中"
type="success"
show-icon
:closable="false"
/>
<div class style="width: 90%">
<el-form ref="form" label-width="135px">
<el-form-item label="开启状态" required>
<el-radio v-model="formData.mnp_notice.status" :label="'0'">关闭</el-radio>
<el-radio v-model="formData.mnp_notice.status" :label="'1'">开启</el-radio>
</el-form-item>
<el-form-item label="模板ID" size="mini" required>
<el-input v-model="formData.mnp_notice.template_id"></el-input>
</el-form-item>
<el-form-item label="模板内容" size="mini" required>
<el-button type="primary" size="mini" @click="onAddWeChatModeField"
>新增模板字段</el-button
>
<el-table
class="m-t-12"
:data="formData.mnp_notice.tpl"
style="width: 100%"
size="mini"
>
<el-table-column label="字段名" width="120">
<template #="scope">
<el-input
v-model="formData.mnp_notice.tpl[scope.$index].tpl_name"
placeholder="例如:订单编号"
></el-input>
</template>
</el-table-column>
<el-table-column label="字段值" width="120">
<template #="scope">
<el-input
v-model="formData.mnp_notice.tpl[scope.$index].tpl_keyword"
placeholder="例如:keyword1.DT"
></el-input>
</template>
</el-table-column>
<el-table-column label="字段内容" width="180">
<template #="scope">
<el-input
v-model="formData.mnp_notice.tpl[scope.$index].tpl_content"
placeholder="例如:${order.sn}"
></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #="scope">
<el-button
type="text"
size="mall"
@click="formData.mnp_notice.tpl.splice(scope.$index, 1)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div class="desc m-t-20" style="margin-left: 135px">
<div v-for="(item, index) in formData.mnp_notice.tips" :key="index">{{ item }}</div>
</div>
</div>
</el-card>
<!-- 微信小程序提醒 End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit()">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { apiNoticeDetail, apiNoticeEdit } from '@/api/application'
import { useAdmin } from '@/core/hooks/app'
import FooterBtns from '@/components/footer-btns/index.vue'
/** Data Start **/
const { router, route } = useAdmin()
const id = ref<any>(route.query.id)
const formData = ref<any>({
// 系统通知
system_notice: {
is_show: false,
content: '',
status: '',
title: '',
},
// 短信通知
sms_notice: {
content: '',
is_show: true,
status: '',
template_id: '',
},
// 微信模板
oa_notice: {
first: '',
is_show: false,
name: '',
remark: '',
status: '',
template_id: '',
template_sn: '',
tpl: [],
},
// 微信小程序
mnp_notice: {
is_show: false,
name: '',
status: '',
template_id: '',
template_sn: '',
tpl: [],
},
})
/** Data End **/
/** Methods Start **/
// 提交保存
const onSubmit = async (): Promise<void> => {
const params = {
id: id.value,
template: [
{
type: 'system',
...formData.value.system_notice,
},
{
type: 'sms',
...formData.value.sms_notice,
},
{
type: 'oa',
...formData.value.oa_notice,
},
{
type: 'mnp',
...formData.value.mnp_notice,
},
],
}
await apiNoticeEdit({ ...params })
router.back()
}
// 获取详情
const getNoticeDetail = async (): Promise<void> => {
formData.value = await apiNoticeDetail({ id: id.value })
}
// 新增微信模板字段
const onAddModeField = (): void => {
formData.value.oa_notice.tpl.push({
tpl_name: '',
tpl_keyword: '',
tpl_content: '',
})
}
// 新增微信小程序模板字段
const onAddWeChatModeField = (): void => {
formData.value.mnp_notice.tpl.push({
tpl_name: '',
tpl_keyword: '',
tpl_content: '',
})
}
/** Methods End **/
/** Life Cycle Start **/
if (id.value) getNoticeDetail()
/** Life Cycle End **/
</script>
<style lang="scss" scoped>
::v-deep .text .el-textarea__inner {
height: 100px !important;
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<el-card shadow="never">
<!-- Header Alert Start -->
<el-alert
title="温馨提示: 1.平台配置在各个场景下的通知发送方式和内容模板。"
type="primary"
:closable="false"
/>
<!-- Header Alert End -->
</el-card>
<el-card shadow="never" class="m-t-15">
<!-- Main TableData Start -->
<el-tabs v-model="formData.recipient" @click="changeTabs">
<el-tab-pane label="通知用户" :name="1"></el-tab-pane>
<el-tab-pane label="通知平台" :name="2"></el-tab-pane>
</el-tabs>
<el-table
ref="tableDataRef"
class="m-t-15"
:data="pager.lists"
style="width: 100%"
size="mini"
>
<el-table-column property="scene_name" label="通知名称" max-width="200" />
<el-table-column property="type_desc" label="通知类型" max-width="355" />
<el-table-column label="短信通知" max-width="300">
<template #default="scope">
<!-- 短信通知的当前状态 -->
<el-tag
:type="scope.row.sms_status_desc == '停用' ? 'danger' : 'success'"
effect="plain"
>{{ scope.row.sms_status_desc }}</el-tag
>
</template>
</el-table-column>
<el-table-column label="操作" max-width="300">
<template #default="scope">
<router-link
class="m-r-10"
:to="{
path: '/application/notification/detail',
query: {
id: scope.row.id,
},
}"
>
<el-button type="text">设置</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
<!-- Main TableData End -->
<!-- Footer Pagination Start -->
<div class="flex row-right">
<pagination
v-model="pager"
@change="requestApi"
layout="total, prev, pager, next, jumper"
/>
</div>
<!-- Footer Pagination End -->
</el-card>
</template>
<script lang="ts" setup>
import { apiNoticeLists } from '@/api/application'
import { ref } from 'vue'
import Pagination from '@/components/pagination/index.vue'
import { usePages } from '@/core/hooks/pages'
/** Data Start **/
let formData = ref({
recipient: 1 as number,
})
const { pager, requestApi } = usePages({
callback: apiNoticeLists,
params: formData.value,
})
/** Data End **/
/** Methods Start **/
const changeTabs = (): void => {
requestApi()
}
/** Methods End **/
/** LifeCycle Start **/
requestApi()
/** LifeCycle End **/
</script>
<style lang="scss">
.el-tabs__nav-wrap {
background: none !important;
}
.el-tabs__nav-wrap::after {
background: none !important;
}
.el-tabs__item {
margin-right: 30px;
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<!-- Header Start -->
<el-card shadow="never">
<el-page-header @back="$router.back()" content="短信设置" />
</el-card>
<!-- Header End -->
<!-- Main Start -->
<el-card shadow="never" class="m-t-15">
<el-form ref="form" label-width="135px">
<el-form-item label="短信渠道">{{ formData.name }}</el-form-item>
<el-form-item label="开启状态" required>
<el-radio
v-model="formData.status"
:label="typeof formData.status == 'string' ? '0' : 0"
>关闭</el-radio
>
<el-radio
v-model="formData.status"
:label="typeof formData.status == 'string' ? '1' : 1"
>开启</el-radio
>
</el-form-item>
<el-form-item label="短信签名" size="mini" required>
<el-input class="ls-input" v-model="formData.sign"></el-input>
</el-form-item>
<el-form-item v-if="type == 'tencent'" label="APP_ID" size="mini" required>
<el-input class="ls-input" v-model="formData.app_id"></el-input>
</el-form-item>
<el-form-item label="APP_KEY" v-if="type == 'ali'" size="mini" required>
<el-input class="ls-input" v-model="formData.app_key"></el-input>
</el-form-item>
<el-form-item v-if="type == 'tencent'" label="SECRET_ID" size="mini" required>
<el-input class="ls-input" v-model="formData.secret_id"></el-input>
</el-form-item>
<el-form-item label="SECRET_KEY" size="mini" required>
<el-input class="ls-input" v-model="formData.secret_key"></el-input>
</el-form-item>
</el-form>
</el-card>
<!-- Main End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit()">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { apiSmsDetail, apiSmsEdit } from '@/api/application'
import { ref } from 'vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import { useAdmin } from '@/core/hooks/app'
import { number } from 'echarts/core'
/** Interface Start **/
interface formDataObj {
name: string
app_key: string
status: number
sign: string
secret_id: string
app_id: string
secret_key: string
}
/** Interface End **/
/** Data Start **/
const { router, route } = useAdmin()
const type = ref<any>(route.query.type)
const formData = ref<formDataObj>({
name: '',
app_key: '',
status: 1,
sign: '',
secret_id: '',
app_id: '',
secret_key: '',
})
/** Data End **/
/** Methods Start **/
// 详情
const getSmsDetail = async (): Promise<void> => {
;(formData.value as object) = await apiSmsDetail({ type: type.value })
}
const onSubmit = async (): Promise<void> => {
await apiSmsEdit({ ...formData.value, type: type.value })
router.back()
}
/** Methods End **/
/** Life Cycle Start **/
if (type.value) getSmsDetail()
/** Life Cycle End **/
</script>
<style lang="scss" scoped>
.ls-input {
width: 305px;
}
</style>

View File

@@ -0,0 +1,53 @@
<template>
<el-card shadow="never">
<el-table :data="tableData" style="width: 100%" size="mini">
<el-table-column prop="name" label="短信通道" min-width="180" />
<el-table-column label="状态" min-width="180">
<template #="scope">
<!-- 短信通知的当前状态 -->
<el-tag :type="scope.row.status == 0 ? 'danger' : 'success'" effect="plain">{{
scope.row.status == 0 ? '关闭' : '启用'
}}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #="scope">
<router-link
class="m-r-10"
:to="{
path: '/application/sms/detail',
query: {
type: scope.$index === 0 ? 'ali' : 'tencent',
},
}"
>
<el-button type="text">设置</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { apiSmsLists } from '@/api/application'
/** Data Start **/
let tableData = ref<Array<object>>([])
/** Data End **/
/** Methods Start **/
const getSmsLists = async (): Promise<void> => {
const res: any = await apiSmsLists()
tableData.value = [{ ...res.ali }, { ...res.tencent }]
}
/** Methods Start **/
/** Life Cycle Start **/
getSmsLists()
/** Life Cycle Start **/
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,88 @@
<!-- APP商城 -->
<template>
<!-- Main Form Start -->
<el-form ref="formRef" :model="formData" label-width="140px" size="small">
<!-- APP 下载 -->
<el-card shadow="never" class="m-t-16">
<template #header>APP下载</template>
<!-- 提示 -->
<div class="ls-card-alert">
<el-alert
class="xs"
title="苹果APP可通过上架APP至苹果App Store获取下载链接安卓APP可通过上架APP至应用宝获取下载链接下载链接也可使用蒲公英等分发渠道的链接。"
type="primary"
:closable="false"
show-icon
/>
</div>
<el-form-item label="苹果APP下载链接">
<el-input class="ls-input m-r-10" v-model="formData.ios_download_url"></el-input>
</el-form-item>
<el-form-item label="安卓APP下载链接">
<el-input class="ls-input m-r-10" v-model="formData.android_download_url"></el-input>
</el-form-item>
<el-form-item label="APP下载引导文案">
<el-input class="ls-input m-r-10" v-model="formData.download_title"></el-input>
<div class="muted xs m-r-16">分享APP页面打开后H5页面顶部会显示APP下载引导文案</div>
</el-form-item>
</el-card>
</el-form>
<!-- Main Form End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import {
apiAppSettings,
apiAppSettingsSet
} from '@/api/channel/app_store'
import { AppSettings_Req } from '@/api/channel/app_store.d.ts'
import FooterBtns from '@/components/footer-btns/index.vue'
/** Data Start **/
const formData = ref<AppSettings_Req>({
ios_download_url: '', // 苹果APP下载链接
android_download_url: '', // 安卓APP下载链接
download_title: '', // APP下载引导文案
})
/** Data End **/
/** Methods Start **/
// 获取APP配置
const getAppSettings = async (): Promise<void> => {
formData.value = await apiAppSettings()
}
// 编辑App配置
const handleAppStoreEdit = async (): Promise<void> => {
await apiAppSettingsSet({ ...formData.value })
getAppSettings()
}
// 提交数据
const onSubmit = (): void => {
handleAppStoreEdit()
}
/** Methods End **/
/** LifeCycle Start **/
getAppSettings()
/** LifeCycle End **/
</script>
<style lang="scss" scoped>
.ls-input {
width: 280px;
}
.ls-card-alert {
border-radius: 8px;
background-color: #ffffff;
padding: 0 24px 24px 24px;
flex: 1;
}
</style>

View File

@@ -0,0 +1,73 @@
<!-- H5 -->
<template>
<!-- Main Form Start -->
<el-form ref="formRef" :model="formData" label-width="140px" size="small">
<!-- 微信小程序 -->
<el-card shadow="never" class="ls-card m-t-16">
<el-form-item label="H5状态">
<!-- switch开关 -->
<div class="flex col-center">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{ formData.status ? '开启' : '关闭' }}</span>
</div>
<div class="muted xs m-r-16">渠道状态为关闭时将不对外提供服务请谨慎操作</div>
</el-form-item>
<el-form-item label="访问链接">
<el-input class="ls-input m-r-10" v-model="formData.url" disabled></el-input>
<el-button v-on:copy="formData.url">复制</el-button>
</el-form-item>
</el-card>
</el-form>
<!-- Main Form End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import {
apiH5Settings,
apiH5SettingsSet
} from '@/api/channel/h5_store'
import FooterBtns from '@/components/footer-btns/index.vue'
/** Interface Start **/
interface formDataObj {
status?: number | string // 状态 0-关闭 1-开启
url: string // 访问链接
}
/** Interface End **/
/** Data Start **/
const formData = ref<formDataObj>({
status: 1,
url: '',
})
/** Data End **/
/** Methods Start **/
// 获取H5设置
const getH5Settings = async (): Promise<void> => {
formData.value = await apiH5Settings()
}
// 修改H5设置
const onSubmit = async (): Promise<void> => {
await apiH5SettingsSet({ status: formData.value.status })
getH5Settings()
}
/** Methods End **/
/** LifeCycle Start **/
getH5Settings()
/** LifeCycle End **/
</script>
<style lang="scss" scoped>
.ls-input {
width: 280px;
}
</style>

View File

@@ -0,0 +1,29 @@
<template>
<div class="home">
<div :style="{color: styleConfig.primary}">{{cartNum}}</div>
<el-button type="primary" @click="addCartNum">主要按钮</el-button>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { Getter, Mutation } from "vuex-class";
@Component({
components: {},
watch: {
cartNum(val) {
console.log(val)
}
}
})
export default class Home extends Vue {
@Getter('cartNum') cartNum!: number
@Mutation('addCartNum') addCartNum!: () => void
created() {
console.log(this.cartNum, )
}
}
</script>

View File

@@ -0,0 +1,126 @@
<template>
<el-form :inline="true" label-position="top" :model="form" :rules="rules" ref="menuFormRef">
<el-form-item label="菜单名称" prop="name" v-if="mode !== 'index'">
<el-input class="ls-input-menu" v-model="form.name"></el-input>
</el-form-item>
<el-form-item :label="mode !== 'index' ? '菜单类型' : ''" prop="type">
<el-select v-model="form.type" placeholder="请选择">
<el-option label="网页" value="view"></el-option>
<el-option label="小程序" value="miniprogram"></el-option>
</el-select>
</el-form-item>
<div v-if="form.type === 'view'">
<el-form-item label="网址" prop="url" required>
<el-input class="ls-input-menu" v-model="form.url"></el-input>
</el-form-item>
</div>
<div v-if="form.type === 'miniprogram'">
<el-form-item label="网址" prop="url" required>
<el-input class="ls-input-menu" v-model="form.url"></el-input>
</el-form-item>
<el-form-item label="AppID" prop="appid" required>
<el-input class="ls-input-menu" v-model="form.appid"></el-input>
</el-form-item>
<el-form-item label="路径" prop="pagepath" required>
<el-input class="ls-input-menu" v-model="form.pagepath"></el-input>
</el-form-item>
</div>
</el-form>
</template>
<script lang="ts" setup>
import { Ref, ref, watch, watchEffect, withDefaults } from 'vue'
/** Emit Start **/
const emit = defineEmits(['update:name', 'update:type', 'update:url', 'update:appid', 'update:pagepath'])
/** Emit End **/
/** Props Start **/
const props = withDefaults(defineProps<{
mode: any
name?: string
type?: string
url?: string
appid?: string
pagepath?: string
}>(), {
mode: 'normal',
name: '',
type: '',
url: '',
appid: '',
pagepath: ''
})
/** Props End **/
/** Data Start **/
const menuFormRef = ref<any>(null)
// 表单数据
let form = ref({...props})
// 表单检验
const rules = ref<object>({
name: [
{ required: true, message: '必填项不能为空', trigger: ['blur', 'change'] },
{ min: 1, max: 18, message: '长度限制18个字符', trigger: ['blur', 'change'] }
],
type: [{ required: true, message: '必填项不能为空', trigger: ['blur', 'change'] }],
url: [
{ required: true, message: '必填项不能为空', trigger: ['blur', 'change'] },
{
pattern: /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/,
message: '请输入合法链接',
trigger: ['blur', 'change']
}
],
appid: [
{
validator: (rule: object, value: string, callback: Function) => {
if (value || form.value.type !== 'miniprogram') callback()
else callback(new Error())
},
message: '必填项不能为空',
trigger: ['blur', 'change']
}
],
pagepath: [
{
validator: (rule: object, value: string, callback: Function) => {
if (value || form.value.type !== 'miniprogram') callback()
else callback(new Error())
},
message: '必填项不能为空',
trigger: ['blur', 'change']
}
],
})
/** Data End **/
/** Watch Start **/
watchEffect(() => {
form.value = props
if(props.mode === 'index') {
emit('update:appid', form.value.appid)
emit('update:url', form.value.url)
emit('update:type', form.value.type)
emit('update:pagepath', form.value.pagepath)
}
})
/** Watch End **/
defineExpose({
menuFormRef,
form,
})
</script>
<style lang="scss" scoped>
:deep .ls-input-menu {
width: 280px;
}
</style>

View File

@@ -0,0 +1,239 @@
<template>
<!-- Header Alert Start -->
<el-card shadow="never">
<el-alert
title="温馨提示:请先前往微信公众号后台申请认证微信公众号-服务号。"
type="primary"
:closable="false"
show-icon
/>
</el-card>
<!-- Header Alert End -->
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" size="small">
<!-- 微信公众号 Start -->
<el-card shadow="never" class="m-t-16">
<template #header>
<span class="nr f-w-500">微信公众号</span>
</template>
<div class="m-t-24">
<el-form-item label="名称" prop="name">
<el-input class="ls-input" v-model="formData.name" show-word-limit />
</el-form-item>
<el-form-item label="原始ID" prop="original_id">
<el-input class="ls-input" v-model="formData.original_id" show-word-limit />
</el-form-item>
<el-form-item label="二维码" prop="qr_code">
<material-select :limit="1" v-model="formData.qr_code" />
<div class="muted xs m-r-16">建议尺寸宽400px*高400pxjpgjpegpng格式</div>
</el-form-item>
</div>
</el-card>
<!-- 微信公众号 End -->
<!-- 公众号开发者信息 Start -->
<el-card shadow="never" class="m-t-16">
<template #header>
<span class="nr f-w-500">公众号开发者信息</span>
</template>
<div class="m-t-24">
<el-form-item label="AppID" prop="app_id">
<el-input class="ls-input" v-model="formData.app_id" show-word-limit />
</el-form-item>
<el-form-item label="AppSecret" prop="app_secret">
<el-input class="ls-input" v-model="formData.app_secret" show-word-limit />
<div class="muted xs m-r-16">登录微信公众平台点击开发>基本配置>公众号开发信息设置AppID和AppSecret</div>
</el-form-item>
</div>
</el-card>
<!-- 公众号开发者信息 End -->
<!-- 服务器配置 Start -->
<el-card shadow="never" class="m-t-16">
<template #header>
<span class="nr f-w-500">服务器配置</span>
</template>
<div class="m-t-24">
<el-form-item label="URL">
<el-input
class="ls-input m-r-16"
v-model="formData.url"
show-word-limit
disabled
/>
<el-button v-on:copy="formData.url">复制</el-button>
<div class="muted xs">登录微信公众平台点击开发>基本配置>服务器配置填写服务器地址URL</div>
</el-form-item>
<el-form-item label="Token" prop="token">
<el-input class="ls-input" v-model="formData.token" show-word-limit />
<div class="muted xs">登录微信公众平台点击开发>基本配置>服务器配置设置令牌Token不填默认为likeshop</div>
</el-form-item>
<el-form-item label="EncodingAESKey" prop="encoding_aes_key">
<el-input class="ls-input" v-model="formData.encoding_aes_key" show-word-limit />
<div class="muted xs">消息加密密钥由43位字符组成字符范围为A-Z,a-z,0-9</div>
</el-form-item>
<el-form-item label="消息加密方式" prop="encryption_type">
<el-radio-group v-model="formData.encryption_type">
<el-radio :label="1" class="form__item-encryption">明文模式 (不使用消息体加解密功能安全系数较低)</el-radio>
<el-radio
:label="2"
class="form__item-encryption"
>兼容模式 (明文密文将共存方便开发者调试和维护)</el-radio>
<el-radio
:label="3"
class="form__item-encryption"
>安全模式推荐 (消息包为纯密文需要开发者加密和解密安全系数高)</el-radio>
</el-radio-group>
</el-form-item>
</div>
</el-card>
<!-- 服务器配置 End -->
<!-- 功能设置 Start -->
<el-card shadow="never" class="m-t-16">
<template #header>
<span class="nr f-w-500">功能设置</span>
</template>
<div class="m-t-24">
<el-form-item label="业务域名">
<el-input
class="ls-input m-r-16"
v-model="formData.business_domain"
show-word-limit
disabled
/>
<el-button class="m-l-16" v-on:copy="formData.business_domain">复制</el-button>
<div class="muted xs">登录微信公众平台点击设置>公众号设置>功能设置填写业务域名</div>
</el-form-item>
<el-form-item label="JS接口安全域名">
<el-input
class="ls-input m-r-16"
v-model="formData.js_secure_domain"
show-word-limit
disabled
/>
<el-button class="m-l-16" v-on:copy="formData.js_secure_domain">复制</el-button>
<div class="muted xs">登录微信公众平台点击设置>公众号设置>功能设置填写JS接口安全域名</div>
</el-form-item>
<el-form-item label="网页授权域名">
<el-input
class="ls-input m-r-16"
v-model="formData.web_auth_domain"
show-word-limit
disabled
/>
<el-button v-on:copy="formData.web_auth_domain">复制</el-button>
<div class="muted xs">登录微信公众平台点击设置>公众号设置>功能设置填写网页授权域名</div>
</el-form-item>
</div>
</el-card>
<!-- 功能设置 Start -->
</el-form>
<!-- Footer Start -->
<footer-btns>
<el-button size="small" @click="onResetFrom">重置</el-button>
<el-button type="primary" size="small" @click="onSubmitFrom(formRef)">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import MaterialSelect from '@/components/material-select/index.vue'
import { apiMpWeChatConfigEdit, apiMPWeChatConfigInfo } from '@/api/channel/mp_wechat'
import { ElMessage } from 'element-plus'
import type { ElForm } from "element-plus";
import FooterBtns from '@/components/footer-btns/index.vue'
/** Interface Start **/
interface formDataObj {
name: string
original_id: string
qr_code: string
app_id: string
app_secret: string
url?: string
token: string
encoding_aes_key: string
encryption_type: string
business_domain?: string
js_secure_domain?: string
web_auth_domain?: string
}
type FormInstance = InstanceType<typeof ElForm>
const formRef = ref<FormInstance>();
/** Interface End **/
/** Data Start **/
// 表单数据
let formData = ref<formDataObj>({
name: '', // 公众号名称
original_id: '', // 原始id
qr_code: '', // 二维码
app_id: '', // APP ID
app_secret: '', // App Secret
url: '', // URL
token: '', // Token
encoding_aes_key: '', // Encoding AES Key
encryption_type: '', // 消息加密方式: 1-明文模式 2-兼容模式 3-安全模式
business_domain: '', // 业务域名
js_secure_domain: '', // JS接口安全域名
web_auth_domain: '' // 网页授权域名
})
// 表单验证
const rules = reactive<object>({
app_id: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
app_secret: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
encryption_type: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
})
/** Data End **/
/** S Methods **/
// 初始化表单数据
const initFormData = async (): Promise<void> => {
formData.value = await apiMPWeChatConfigInfo()
console.log(formData.value)
}
// 重置表单数据
const onResetFrom = () => {
initFormData()
ElMessage.info('已重置数据')
}
// 编辑数据
const handleFormEdit = async (): Promise<void> => {
await apiMpWeChatConfigEdit({ ...formData.value })
initFormData()
}
// 提交表单
const onSubmitFrom = async (formEl: FormInstance | undefined): Promise<void> => {
if (!formEl) return
formEl.validate((valid): boolean | undefined => {
if (!valid) return false
handleFormEdit()
})
}
/** E Methods **/
/** S Life Cycle **/
initFormData()
/** E Life Cycle **/
</script>
<style lang="scss" scoped>
.ls-input {
width: 280px;
}
.form__item-encryption {
display: flex;
align-items: center;
height: 3em;
}
</style>

View File

@@ -0,0 +1,392 @@
<template>
<!-- Header Alert Start -->
<el-card shadow="never">
<el-alert
title="温馨提示:点击保存并发布菜单后,菜单才会发布至微信公众号,需提前设置好公众号相关配置。"
type="primary"
:closable="false"
show-icon
/>
</el-card>
<!-- Header Alert End -->
<div class="flex m-t-16">
<!-- Phone Start -->
<el-card shadow="never">
<div class="mp_wechat__phone">
<div class="mp_wechat__phone-menu mp_wechat__phone-active">
<div
class="mp_wechat__phone-menu-item"
v-for="(item, index) in formData.menu"
:key="index"
>
<div class="mp_wechat__phone-menu-item--title">
<span>{{ item.name }}</span>
</div>
<div class="mp_wechat__phone-submenu" v-show="item.has_menu">
<div
class="mp_wechat__phone-submenu-item"
v-for="(item2, index2) in item.sub_button"
:key="index2"
>{{ item2.name }}</div>
</div>
</div>
</div>
</div>
</el-card>
<!-- Phone End -->
<!-- Menu Config Start -->
<el-card shadow="never" class="m-l-16 mp_wechat__form">
<div class="mp_wechat__form--title">菜单配置</div>
<div class="m-t-16">
<el-button type="primary" plain size="small" @click="onMenuAdd">
<i class="el-icon-plus"></i>
<span>新增主菜单{{ formData.menu.length || 0 }}/3</span>
</el-button>
</div>
<div class="mp_wechat__form--content m-t-24">
<div class="menu-item" v-for="(item, index) in formData.menu" :key="index">
<!-- 删除菜单 -->
<div class="menu-item__delete">
<popup class="m-l-10 inline" top="35vh" @confirm="handleMenuDel(index)">
<template #trigger>
<el-icon size="16">
<close />
</el-icon>
</template>
</popup>
</div>
<el-form
ref="formRef"
:model="formData.menu[index]"
:rules="rules"
label-position="top"
size="small"
>
<!-- 主菜单名称 -->
<el-form-item label="主菜单" prop="name">
<el-input
class="ls-input"
v-model="formData.menu[index].name"
show-word-limit
/>
</el-form-item>
<!-- 主菜单类型 -->
<el-form-item label="主菜单类型">
<el-radio-group v-model="formData.menu[index].has_menu">
<el-radio :label="false">不配置子菜单</el-radio>
<el-radio :label="true">配置子菜单</el-radio>
</el-radio-group>
</el-form-item>
<!-- 没有子菜单 -->
<div v-if="!formData.menu[index].has_menu">
<MPWechatMenuForm
mode="index"
v-model:url="formData.menu[index].url"
v-model:appid="formData.menu[index].appid"
v-model:type="formData.menu[index].type"
v-model:pagepath="formData.menu[index].pagepath"
/>
</div>
<!-- 存在子菜单 -->
<div v-if="formData.menu[index].has_menu">
<ul>
<li
class="flex"
v-for="(subItem, subIndex) in formData.menu[index].sub_button"
:key="subIndex"
style="padding: 8px;"
>
<span style="margin-right: auto">{{ subItem.name }}</span>
<!-- 编辑子菜单 -->
<popup
top="40vh"
title="子菜单"
:async="true"
:clickModalClose="false"
@close="handleSubMenuReset"
@confirm="onSubMenuEdit(index, subIndex)"
:ref="(el: Event | any) => subMenuFormEditPopupRef = el"
>
<MPWechatMenuForm
:name="subItem.name"
:url="subItem.url"
:appid="subItem.appid"
:type="subItem.type"
:pagepath="subItem.pagepath"
:ref="(el: Event | any) => subMenuFormEditRef = el"
/>
<template #trigger>
<el-icon size="16">
<Edit />
</el-icon>
</template>
</popup>
<!-- 删除子菜单 -->
<popup
class="m-l-10 inline"
top="35vh"
@confirm="handleSubMenuDel(index, subIndex)"
>
<template #trigger>
<el-icon size="16">
<close />
</el-icon>
</template>
</popup>
</li>
</ul>
<popup
top="40vh"
:async="true"
:clickModalClose="false"
title="新增子菜单"
:ref="(el: Event | any) => subMenuFormAddPopupRef = el"
@confirm="handleSubMenuAdd(index)"
>
<MPWechatMenuForm
:ref="(el: Event | any) => subMenuFormAddRef = el"
/>
<template #trigger>
<el-button
type="text"
size="small"
>添加子菜单{{ formData.menu[index].sub_button.length || 0 }}/5</el-button>
</template>
</popup>
</div>
</el-form>
</div>
</div>
</el-card>
<!-- Menu Config Start -->
</div>
<!-- Footer Start -->
<footer-btns>
<el-button size="small" @click="onFromSave">保存</el-button>
<el-button type="primary" size="small" @click="onFromPublish">保存并发布</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { apiMpWeChatMenuDetail, apiMpWeChatMenuSave, apiMpWeChatMenuPublish } from '@/api/channel/mp_wechat'
import { MPWeChatMenu } from '@/api/channel/mp_wechat.d.ts'
import Popup from '@/components/popup/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import { ElMessage } from "element-plus";
import MPWechatMenuForm from './components/menu-form.vue'
import { deepClone } from '@/utils/util'
/** Interface Start **/
interface MenuFrom {
menu: Array<MPWeChatMenu>
}
/** Interface Start **/
/** Data Start **/
// 子菜单添加的Ref
const subMenuFormAddRef = ref<InstanceType<typeof MPWechatMenuForm> | null>(null)
// 子菜单添加的弹出窗口
const subMenuFormAddPopupRef = ref<InstanceType<typeof Popup> | null>(null)
// 子菜单编辑的Ref
const subMenuFormEditRef = ref<InstanceType<typeof MPWechatMenuForm> | null>(null)
// 子菜单编辑的弹出窗口
const subMenuFormEditPopupRef = ref<InstanceType<typeof Popup> | null>(null)
// 菜单数据
const formData = ref<MenuFrom>({
menu: []
})
// 校验
const rules = reactive<object>({
name: [
{ required: true, message: '必填项不能为空', trigger: ['blur', 'change'] },
{ min: 1, max: 12, message: '长度限制12个字符', trigger: ['blur', 'change'] }
],
})
/** Data End **/
/** Methods Start **/
// 添加主菜单
const onMenuAdd = () => {
if (formData.value.menu.length >= 3)
return ElMessage.info('主菜单仅限有3项!')
formData.value.menu.push({
name: '',
type: '',
has_menu: false,
key: '', // 关键字
url: '', // 网页URL
appid: '', // 小程序AppID
pagepath: '', // 小程序路径
sub_button: [] as Array<object>, // 二级菜单
})
}
// 添加子菜单
const handleSubMenuAdd = async (index: number): Promise<void> => {
await subMenuFormAddRef.value?.menuFormRef.validate()
formData.value.menu[index].sub_button.push({ ...deepClone(subMenuFormAddRef.value?.menuFormRef.model) })
ElMessage.success('添加成功')
subMenuFormAddPopupRef.value?.close()
subMenuFormAddRef.value?.menuFormRef.resetFields()
}
// 子菜单编辑
const onSubMenuEdit = async (index: number, subIndex: number): Promise<void> => {
await subMenuFormEditRef.value?.menuFormRef.validate()
formData.value.menu[index].sub_button[subIndex] = { ...deepClone(subMenuFormEditRef.value?.menuFormRef.model) }
ElMessage.success('修改成功')
subMenuFormEditRef.value?.menuFormRef.resetFields()
subMenuFormEditPopupRef.value?.close()
}
// 取消编辑恢复数据
const handleSubMenuReset = (): void => {
subMenuFormEditRef.value?.menuFormRef.resetFields()
}
// 删除菜单
const handleMenuDel = (index: number): void => {
formData.value?.menu.splice(index, 1)
}
// 删除子菜单
const handleSubMenuDel = (index: number, subIndex: number): void => {
const menu: MPWeChatMenu = formData.value.menu[index]
menu.sub_button.splice(subIndex, 1)
}
// 初始化菜单数据
const initMPWeChatMenuData = async (): Promise<void> => {
formData.value.menu = await apiMpWeChatMenuDetail()
}
// 表单保存
const onFromSave = async (): Promise<void> => {
await apiMpWeChatMenuSave({ ...formData.value })
initMPWeChatMenuData()
}
// 保存并发布
const onFromPublish = async (): Promise<void> => {
await apiMpWeChatMenuPublish({ ...formData.value })
initMPWeChatMenuData()
}
/** Methods End **/
/** Life Cycle Start **/
initMPWeChatMenuData()
/** Life Cycle End **/
</script>
<style lang="scss" scoped>
.mp_wechat__phone {
position: relative;
width: 312.5px;
height: 676.67px;
background: url("../../../assets/images/mobile.png") no-repeat;
background-size: 100% 100%;
&-menu {
position: absolute;
left: 0;
right: 0;
bottom: 40px;
display: flex;
height: 60px;
margin: 0 16px;
border-top: 1px solid #e4e4e4;
background-color: rgb(247, 247, 247);
&-item {
flex: 1;
position: relative;
display: flex;
justify-content: center;
align-items: center;
&:nth-child(n + 2)::before {
position: absolute;
left: 0;
height: 50%;
display: block;
content: "";
border-left: 1px solid #e4e4e4;
}
&--title {
font-weight: 500;
}
}
.mp_wechat__phone-submenu {
position: absolute;
z-index: 99;
bottom: calc(100% + 10px);
min-width: 100px;
background-color: rgb(247, 247, 247);
box-shadow: 0 0 4px 1px rgba(0, 0, 0, 0.3);
&-item {
padding: 16px 4px;
}
&-item:nth-child(n + 2) {
border-top: 1px solid #e4e4e4;
}
}
}
&-active {
border: 2px dashed $color-primary;
}
}
.mp_wechat__form {
width: 100%;
height: 100%;
flex: 1;
&--title {
font-size: 14px;
font-weight: 500;
}
&--content {
display: flex;
flex-wrap: wrap;
.menu-item {
position: relative;
box-sizing: border-box;
width: 340px;
padding: 24px;
border-radius: 8px;
margin-right: 16px;
margin-bottom: 16px;
background-color: #efefef;
overflow: hidden;
&__delete {
position: absolute;
top: 14px;
right: 20px;
color: $color-primary;
cursor: pointer;
}
}
}
}
</style>

View File

@@ -0,0 +1,129 @@
<template>
<!-- Header Alert Start -->
<el-card shadow="never">
<el-alert
class="xxl"
title="温馨提示1.粉丝在公众号发送内容时系统无法匹配情况下发送启用的默认文本回复2.同时只能启用一个默认回复。"
type="primary"
:closable="false"
show-icon
></el-alert>
</el-card>
<!-- Header Alert End -->
<el-card shadow="never" class="m-t-20">
<!-- Header BtnGroup Start -->
<el-button size="small" type="primary" @click="onReplyAdd()">新增默认回复</el-button>
<!-- Header BtnGroup Start -->
<!-- Main TableData Start -->
<div class="m-t-16">
<el-table
:data="pager.lists"
style="width: 100%"
v-loading="pager.loading"
:default-sort="{ prop: 'level', order: 'ascending' }"
:header-cell-style="{ 'background': '#f5f8ff' }"
size="mini"
>
<el-table-column prop="name" label="规则名称" min-width="100px"></el-table-column>
<el-table-column prop="content_type_desc" label="回复类型" min-width="100px"></el-table-column>
<el-table-column prop="status" label="启用状态" min-width="100px">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="putMpWeChatReplyStatus(scope.row)"
/>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" min-width="120px">
<template #default="scope">
<!-- <el-button type="text" size="small" @click="onUserLevelEdit(scope.row)">详情</el-button> -->
<el-button type="text" size="small" @click="onReplyEdit(scope.row)">编辑</el-button>
<popup class="m-l-10 inline" @confirm="onMpWeChatReplyDelete(scope.row)">
<el-button type="text" size="small" slot="trigger">删除</el-button>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<!-- Main TableData End -->
<!-- Footer Pagination Start -->
<div class="flex row-right">
<pagination v-model="pager" @change="requestApi" layout="total, prev, pager, next, jumper" />
</div>
<!-- Footer Pagination End -->
</el-card>
</template>
<script lang="ts" setup>
import { ref } from "vue"
import {
apiMpWeChatReplyLists,
apiMpWeChatReplyDelete,
apiMpWeChatReplyStatus
} from "@/api/channel/mp_wechat"
import { PageMode } from "@/utils/type"
import Popup from "@/components/popup/index.vue"
import { useAdmin } from "@/core/hooks/app"
import { usePages } from "@/core/hooks/pages"
import Pagination from "@/components/pagination/index.vue"
/** Data Start **/
const { router } = useAdmin();
let formData = ref({ reply_type: '3' as string | undefined | number }) // 回复类型 1-关注回复 2-关键词回复 3-默认回复
const {
pager,
requestApi
} = usePages({
callback: apiMpWeChatReplyLists,
params: formData.value
});
/** Data End **/
/** S Methods **/
// 新增用户等级
const onReplyAdd = (): void => {
router.push({
path: "/channel/mp_wechat/reply/reply_edit",
query: {
mode: PageMode["ADD"],
replyType: formData.value.reply_type,
},
});
}
// 编辑
const onReplyEdit = (event: Event | any): void => {
router.push({
path: "/channel/mp_wechat/reply/reply_edit",
query: {
mode: PageMode["EDIT"],
id: event.id,
replyType: formData.value.reply_type,
},
});
}
// 修改状态
const putMpWeChatReplyStatus = async (event: Event | any): Promise<void> => {
await apiMpWeChatReplyStatus({ id: event.id as number })
requestApi();
}
// 删除
const onMpWeChatReplyDelete = async (event: Event | any): Promise<void> => {
await apiMpWeChatReplyDelete({ id: event.id as number })
requestApi();
}
/** Methods End **/
/** Life Cycle Start **/
requestApi();
/** Life Cycle End **/
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,128 @@
<template>
<!-- Header Alert Start -->
<el-card shadow="never">
<el-alert
class="xxl"
title="温馨提示1.粉丝关注公众号时会自动发送启用的关注回复2.同时只能启用一个关注回复。"
type="primary"
:closable="false"
show-icon
></el-alert>
</el-card>
<!-- Header Alert End -->
<el-card shadow="never" class="m-t-20">
<!-- Header BtnGroup Start -->
<el-button size="small" type="primary" @click="onReplyAdd()">新增关注回复</el-button>
<!-- Header BtnGroup Start -->
<!-- Main TableData Start -->
<div class="m-t-16">
<el-table
:data="pager.lists"
style="width: 100%"
v-loading="pager.loading"
:default-sort="{ prop: 'level', order: 'ascending' }"
:header-cell-style="{ 'background': '#f5f8ff' }"
size="mini"
>
<el-table-column prop="name" label="规则名称" min-width="100px"></el-table-column>
<el-table-column prop="content_type_desc" label="回复类型" min-width="100px"></el-table-column>
<el-table-column prop="status" label="启用状态" min-width="100px">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="putMpWeChatReplyStatus(scope.row)"
/>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" min-width="120px">
<template #default="scope">
<!-- <el-button type="text" size="small" @click="onUserLevelEdit(scope.row)">详情</el-button> -->
<el-button type="text" size="small" @click="onReplyEdit(scope.row)">编辑</el-button>
<popup class="m-l-10 inline" @confirm="onMpWeChatReplyDelete(scope.row)">
<el-button type="text" size="small" slot="trigger">删除</el-button>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<!-- Main TableData End -->
<!-- Footer Pagination Start -->
<div class="flex row-right">
<pagination v-model="pager" @change="requestApi" layout="total, prev, pager, next, jumper" />
</div>
<!-- Footer Pagination End -->
</el-card>
</template>
<script lang="ts" setup>
import { ref } from "vue"
import {
apiMpWeChatReplyLists,
apiMpWeChatReplyDelete,
apiMpWeChatReplyStatus
} from "@/api/channel/mp_wechat"
import { PageMode } from "@/utils/type"
import Popup from "@/components/popup/index.vue"
import { useAdmin } from "@/core/hooks/app"
import { usePages } from "@/core/hooks/pages";
import Pagination from "@/components/pagination/index.vue"
/** Data Start **/
const { router } = useAdmin();
let formData = ref({ reply_type: '1' as string | undefined | number }) // 回复类型 1-关注回复 2-关键词回复 3-默认回复
const {
pager,
requestApi
} = usePages({
callback: apiMpWeChatReplyLists,
params: formData.value
});
/** Data End **/
/** S Methods **/
// 新增用户等级
const onReplyAdd = (): void => {
router.push({
path: "/channel/mp_wechat/reply/reply_edit",
query: {
mode: PageMode["ADD"],
replyType: formData.value.reply_type,
},
});
}
// 编辑
const onReplyEdit = (event: Event | any): void => {
router.push({
path: "/channel/mp_wechat/reply/reply_edit",
query: {
mode: PageMode["EDIT"],
id: event.id,
replyType: formData.value.reply_type,
},
});
}
// 修改状态
const putMpWeChatReplyStatus = async (event: Event | any): Promise<void> => {
await apiMpWeChatReplyStatus({ id: event.id as number })
requestApi();
}
// 删除
const onMpWeChatReplyDelete = async (event: Event | any): Promise<void> => {
await apiMpWeChatReplyDelete({ id: event.id as number })
requestApi();
}
/** Methods End **/
/** Life Cycle Start **/
requestApi();
/** Life Cycle End **/
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,127 @@
<template>
<!-- Header Alert Start -->
<el-card shadow="never">
<el-alert
class="xxl"
title="温馨提示1.粉丝在公众号发送内容时通过关键词可触发关键词回复2.同时可启用多个关键词回复,有多条关键词匹配时优选选择排序靠前的一条。"
type="primary"
:closable="false"
show-icon
></el-alert>
</el-card>
<!-- Header Alert End -->
<el-card shadow="never" class="m-t-20">
<!-- Header BtnGroup Start -->
<el-button size="small" type="primary" @click="onReplyAdd()">新增关键字回复</el-button>
<!-- Header BtnGroup Start -->
<!-- Main TableData Start -->
<div class="m-t-16">
<el-table
:data="pager.lists"
style="width: 100%"
v-loading="pager.loading"
:default-sort="{ prop: 'level', order: 'ascending' }"
:header-cell-style="{ 'background': '#f5f8ff' }"
size="mini"
>
<el-table-column prop="name" label="规则名称" min-width="100px"></el-table-column>
<el-table-column prop="content_type_desc" label="回复类型" min-width="100px"></el-table-column>
<el-table-column prop="status" label="启用状态" min-width="100px">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="putMpWeChatReplyStatus(scope.row)"
/>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" min-width="120px">
<template #default="scope">
<!-- <el-button type="text" size="small" @click="onUserLevelEdit(scope.row)">详情</el-button> -->
<el-button type="text" size="small" @click="onReplyEdit(scope.row)">编辑</el-button>
<popup class="m-l-10 inline" @confirm="onMpWeChatReplyDelete(scope.row)">
<el-button type="text" size="small" slot="trigger">删除</el-button>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<!-- Main TableData End -->
<!-- Footer Pagination Start -->
<div class="flex row-right">
<pagination v-model="pager" @change="requestApi" layout="total, prev, pager, next, jumper" />
</div>
<!-- Footer Pagination End -->
</el-card>
</template>
<script lang="ts" setup>
import { ref } from "vue"
import {
apiMpWeChatReplyLists,
apiMpWeChatReplyDelete,
apiMpWeChatReplyStatus
} from "@/api/channel/mp_wechat"
import { PageMode } from "@/utils/type"
import Popup from "@/components/popup/index.vue"
import { useAdmin } from "@/core/hooks/app"
import { usePages } from "@/core/hooks/pages";
import Pagination from "@/components/pagination/index.vue"
/** Data Start **/
const { router } = useAdmin();
let formData = ref({ reply_type: '2' as string | undefined | number }) // 回复类型 1-关注回复 2-关键词回复 3-默认回复
const {
pager,
requestApi
} = usePages({
callback: apiMpWeChatReplyLists,
params: formData.value
});
/** Data End **/
/** S Methods **/
// 新增用户等级
const onReplyAdd = (): void => {
router.push({
path: "/channel/mp_wechat/reply/reply_edit",
query: {
mode: PageMode["ADD"],
replyType: formData?.value?.reply_type,
},
});
}
// 编辑
const onReplyEdit = (event: Event | any): void => {
router.push({
path: "/channel/mp_wechat/reply/reply_edit",
query: {
mode: PageMode["EDIT"],
id: event.id,
replyType: formData.value.reply_type,
},
});
}
// 修改状态
const putMpWeChatReplyStatus = async (event: Event | any): Promise<void> => {
await apiMpWeChatReplyStatus({ id: event.id as number })
requestApi();
}
// 删除
const onMpWeChatReplyDelete = async (event: Event | any): Promise<void> => {
await apiMpWeChatReplyDelete({ id: event.id as number })
requestApi();
}
/** Methods End **/
/** Life Cycle Start **/
requestApi();
/** Life Cycle End **/
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,192 @@
<!-- 新增/编辑-->
<template>
<!-- Header Start -->
<el-card shadow="never">
<el-page-header
v-if="formData.reply_type == 1"
@back="$router.back()"
:content="mode === 'add' ? '新增关注回复' : '编辑关注回复'"
/>
<el-page-header
v-if="formData.reply_type == 2"
@back="$router.back()"
:content="mode === 'add' ? '新增关键字回复' : '编辑关键字回复'"
/>
<el-page-header
v-if="formData.reply_type == 3"
@back="$router.back()"
:content="mode === 'add' ? '新增默认回复' : '编辑默认回复'"
/>
</el-card>
<!-- Header End -->
<!-- Main Start -->
<el-form :rules="rules" ref="formRef" :model="formData" label-width="120px" size="small">
<el-card shadow="never" class="m-t-16">
<div class="card-title">关注回复</div>
<div class="card-content m-t-24">
<el-form-item label="规则名称" prop="name">
<el-input class="ls-input" v-model="formData.name" placeholder="请输入规则名称"></el-input>
<div class="muted xs">方便通过名称管理关注回复内容</div>
</el-form-item>
<el-form-item v-if="formData.reply_type == 2" label="关键词" prop="keyword">
<el-input class="ls-input" v-model="formData.keyword" placeholder="请输入关键词"></el-input>
</el-form-item>
<el-form-item v-if="formData.reply_type == 2" label="排序值" prop="sort">
<el-input class="ls-input" v-model.number="formData.sort" placeholder="请输入排序值"></el-input>
<div class="muted xs">关键词排序值</div>
</el-form-item>
<el-form-item v-if="formData.reply_type == 2" label="匹配方式" prop="matching_type">
<el-select class="ls-select" v-model="formData.matching_type" placeholder="请选择匹配方式">
<el-option label="全匹配" :value="1"></el-option>
<el-option label="模糊匹配" :value="2"></el-option>
</el-select>
<div class="muted xs">模糊匹配时关键词部分匹配用户输入的内容即可</div>
</el-form-item>
<el-form-item label="内容类型" prop="content_type">
<el-select class="ls-select" v-model="formData.content_type" placeholder="请选择内容类型">
<el-option label="文本" :value="1"></el-option>
</el-select>
<div class="muted xs">暂时支持文本类型</div>
</el-form-item>
<el-form-item label="回复内容" prop="content">
<el-input
class="ls-input-textarea"
v-model="formData.content"
placeholder="请输入回复内容"
type="textarea"
:rows="6"
></el-input>
</el-form-item>
<el-form-item label="启用状态">
<div class="flex col-center">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{ formData.status ? '开启' : '关闭' }}</span>
</div>
</el-form-item>
</div>
</el-card>
</el-form>
<!-- Main End -->
<!-- Footer Start -->
<footer-btns>
<el-button size="small" @click="$router.back()">取消</el-button>
<el-button type="primary" size="small" @click="onSubmit(formRef)">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue"
import {
PageMode
} from '@/utils/type'
import {
apiMpWeChatReplyAdd,
apiMpWeChatReplyEdit,
apiMpWeChatReplyDetail
} from "@/api/channel/mp_wechat"
import FooterBtns from '@/components/footer-btns/index.vue'
import { useAdmin } from '@/core/hooks/app'
import type { ElForm } from "element-plus";
/** Interface Start **/
interface formDataObj {
reply_type?: string | boolean | number
name: string | null
content_type: number
content: string
status: number
keyword: string
matching_type: number
sort: string | number
reply_num: number
}
type FormInstance = InstanceType<typeof ElForm>
const formRef = ref<FormInstance>();
/** Interface End **/
/** Data Start **/
const { router, route } = useAdmin()
const mode = ref<string>(PageMode['ADD']) // 当前页面【add: 添加用户等级 | edit: 编辑用户等级】
const identity = ref(route.query.id as string) // 当前编辑回复ID valid: mode = 'edit'
let formData = ref<formDataObj>({
reply_type: route.query.replyType as string, // 回复类型 1-关注回复 2-关键词回复 3-默认回复
name: '', // 规格名称
content_type: 1, // 内容类型 1-文本
content: '', // 内容
status: 1, // 启用状态 0-禁用 1-开启
keyword: '', // 关键词 *关键词回复必填
matching_type: 1, // 匹配方式 1-全匹配 2-模糊匹配 *关键词回复必填
sort: '', // 排序值 *关键词回复必填
reply_num: 1, // 回复数量 *关键词回复必填 1-回复匹配首条
})
const rules = reactive<object>({
name: [{ required: true, message: '请输入规则名称', trigger: 'blur' }],
keyword: [{ required: true, message: '请输入关键词', trigger: 'blur' }],
sort: [
{ required: true, message: '请输入排序值', trigger: 'blur' },
{ type: 'number', min: 1, message: '请输入大于0的数字值', trigger: 'blur' }
],
matching_type: [{ required: true, message: '请选择匹配方式', trigger: 'change' }],
content_type: [{ required: true, message: '请选择内容类型', trigger: 'change' }],
content: [{ required: true, message: '请输入回复内容', trigger: 'blur' }]
})
/** Data End **/
/** Methods Start **/
// 表单提交
const onSubmit = (formEl: FormInstance | undefined): void => {
if (!formEl) return
formEl.validate((valid): boolean | Promise<void> | undefined => {
if (!valid) return false
switch (mode.value) {
case PageMode['ADD']:
return handleMpWeChatReplyAdd()
case PageMode['EDIT']:
return handleMpWeChatReplyEdit()
}
})
}
// 新增
const handleMpWeChatReplyAdd = async (): Promise<void> => {
await apiMpWeChatReplyAdd(formData.value)
setTimeout(() => router.go(-1), 500)
}
// 编辑
const handleMpWeChatReplyEdit = async (): Promise<void> => {
apiMpWeChatReplyEdit({ ...formData.value, id: identity.value })
setTimeout(() => router.go(-1), 500)
}
// 表单初始化数据 [编辑模式] mode => edit
const initMpWeChatReplyDetail = async (): Promise<void> => {
formData.value = await apiMpWeChatReplyDetail({ id: identity.value })
}
/** Methods Start **/
/** Life Cycle Start **/
// 编辑模式:初始化数据
if (route.query.mode === PageMode['EDIT']) {
mode.value = PageMode['EDIT']
initMpWeChatReplyDetail()
}
/** Life Cycle End **/
</script>
<style lang="scss" scoped>
.ls-input,
.ls-select {
width: 300px;
}
.ls-input-textarea {
width: 300px;
}
.card-title {
font-size: 14px;
font-weight: 500;
}
</style>

View File

@@ -0,0 +1,164 @@
<!-- 微信小程序 -->
<template>
<!-- Header Alert Start -->
<el-card shadow="never">
<el-alert title="温馨提示:请先前往微信小程序后台申请认证微信小程序。" type="primary" :closable="false" show-icon />
</el-card>
<!-- Header Alert End -->
<!-- Main Form Start -->
<el-form ref="formRef" :model="formData" :rules="rules" label-width="140px" size="small">
<!-- 微信小程序 Start -->
<el-card shadow="never" class="m-t-16">
<template #header>微信小程序</template>
<el-form-item label="小程序名称">
<el-input class="ls-input" v-model="formData.name" size="small"></el-input>
</el-form-item>
<el-form-item label="原始ID">
<el-input class="ls-input" v-model="formData.original_id" size="small"></el-input>
</el-form-item>
<el-form-item label="小程序码">
<material-select :limit="1" v-model="formData.qr_code" />
<div class="muted xs m-r-16">建议尺寸400*400像素支持jpgjpegpng格式</div>
</el-form-item>
</el-card>
<!-- 微信小程序 End -->
<!-- 开发者ID Start -->
<el-card shadow="never" class="m-t-16">
<template #header>开发者ID</template>
<el-form-item label="AppID" prop="app_id">
<el-input class="ls-input" v-model="formData.app_id" size="small"></el-input>
</el-form-item>
<el-form-item label="AppSecret" prop="app_secret">
<el-input class="ls-input" v-model="formData.app_secret" size="small"></el-input>
<div class="muted xs m-r-16">小程序账号登录微信公众平台点击开发>开发设置->开发者ID设置AppID和AppSecret</div>
</el-form-item>
</el-card>
<!-- 开发者ID End -->
<!-- 服务器域名 Start -->
<el-card shadow="never" class="m-t-16">
<template #header>服务器域名</template>
<el-form-item label="request合法域名">
<el-input class="ls-input m-r-10" v-model="formData.request_domain" size="small" disabled></el-input>
<el-button size="small" v-on:copy="formData.request_domain">复制</el-button>
<div class="muted xs m-r-16">小程序账号登录微信公众平台点击开发>开发设置->服务器域名填写https协议域名</div>
</el-form-item>
<el-form-item label="socket合法域名">
<el-input class="ls-input m-r-10" v-model="formData.socket_domain" size="small" disabled></el-input>
<el-button size="small" v-on:copy="formData.socket_domain">复制</el-button>
<div class="muted xs m-r-16">小程序账号登录微信公众平台点击开发>开发设置->服务器域名填写wss协议域名</div>
</el-form-item>
<el-form-item label="uploadFile合法域名">
<el-input class="ls-input m-r-10" v-model="formData.upload_file_domain" size="small" disabled></el-input>
<el-button size="small" v-on:copy="formData.upload_file_domain">复制</el-button>
<div class="muted xs m-r-16">小程序账号登录微信公众平台点击开发>开发设置->服务器域名填写https协议域名</div>
</el-form-item>
<el-form-item label="downloadFile合法域名">
<el-input class="ls-input m-r-10" v-model="formData.download_file_domain" size="small" disabled></el-input>
<el-button size="small" v-on:copy="formData.download_file_domain">复制</el-button>
<div class="muted xs m-r-16">小程序账号登录微信公众平台点击开发>开发设置->服务器域名填写https协议域名</div>
</el-form-item>
<el-form-item label="udp合法域名">
<el-input class="ls-input m-r-10" v-model="formData.udp_domain" size="small" disabled></el-input>
<el-button size="small" v-on:copy="formData.udp_domain">复制</el-button>
<div class="muted xs m-r-16">小程序账号登录微信公众平台点击开发>开发设置->服务器域名填写udp协议域名</div>
</el-form-item>
</el-card>
<!-- 服务器域名 End -->
<!-- 业务域名 Start -->
<el-card shadow="never" class="m-t-16">
<template #header>业务域名</template>
<el-form-item label="业务域名">
<el-input class="ls-input m-r-10" v-model="formData.business_domain" size="small" disabled></el-input>
<el-button size="small" v-on:copy="formData.business_domain">复制</el-button>
<div class="muted xs m-r-16">小程序账号登录微信公众平台点击开发>开发设置->业务域名填写业务域名</div>
</el-form-item>
</el-card>
<!-- 业务域名 End -->
</el-form>
<!-- Main Form End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit(formRef)">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import MaterialSelect from '@/components/material-select/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import {
apiWechatMiniSetting,
apiWechatMiniSettingSet
} from '@/api/channel/wechat_app'
import {
WechatMiniSetting_Res,
WechatMiniSetting_Req
} from '@/api/channel/wechat_app.d'
import type { ElForm } from "element-plus";
type FormInstance = InstanceType<typeof ElForm>
const formRef = ref<FormInstance>();
/** S Data **/
const formData = ref<WechatMiniSetting_Res>({
name: '', // 小程序名称
original_id: '', // 原始id
qr_code: '', // 二维码
app_id: '',
app_secret: '',
request_domain: '', // request合法域名
socket_domain: '', // socket合法域名
upload_file_domain: '', // uploadFile合法域名
download_file_domain: '', // downloadFile合法域名
udp_domain: '', // udp合法域名
business_domain: '', // 业务域名
url: '',
token: '',
encoding_aes_key: '',
encryption_type: 1, // 消息加密方式 1-明文模式 2-兼容模式 3-安全模式
data_format: 1 // 数据格式 1-JSON 2-XML
})
// 表单验证
const rules = reactive<object>({
app_id: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
app_secret: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
})
/** Data End **/
/** Methods Start **/
// 获取小程序配置
const getWechatAppSetting = async (): Promise<void> => {
formData.value = await apiWechatMiniSetting()
}
// 编辑小程序配置
const handleWechatAppEdit = async (): Promise<void> => {
await apiWechatMiniSettingSet({ ...formData.value })
getWechatAppSetting()
}
// 提交数据
const onSubmit = (formEl: FormInstance | undefined): void => {
if (!formEl) return
formEl.validate((valid): boolean | undefined => {
if (!valid) return false
handleWechatAppEdit()
})
}
/** Methods End **/
/** LifeCycle Start **/
getWechatAppSetting()
/** LifeCycle End **/
</script>
<style lang="scss" scoped>
.ls-input {
width: 280px;
}
</style>

View File

@@ -0,0 +1,98 @@
<!-- 微信开放平台 -->
<template>
<!-- Main Form Start -->
<el-form :rules="formRules" ref="formRef" :model="form" label-width="140px" size="small">
<!-- 微信开放平台 -->
<el-card shadow="never" class="m-t-16">
<template #header>微信开放平台</template>
<!-- 提示 -->
<div class="ls-card-alert">
<el-alert
title="APP需要使用微信授权登录、微信支付等微信生态能力时需要设置关联微信开发平台请填写APP在微信开发平台申请的应用ID等信息。"
type="primary"
:closable="false"
show-icon
/>
</div>
<el-form-item label="AppID" prop="app_id">
<el-input class="ls-input m-r-10" size="small" v-model="form.app_id"></el-input>
</el-form-item>
<el-form-item label="AppSecret" prop="app_secret">
<el-input class="ls-input m-r-10" size="small" v-model="form.app_secret"></el-input>
</el-form-item>
</el-card>
</el-form>
<!-- Main Form End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit(formRef)">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import {
apiWechatPlatformGet,
apiWechatPlatformSet
} from '@/api/channel/wechat_platform'
import { AppSettings_Req } from '@/api/channel/app_store.d.ts'
import type { ElForm } from "element-plus";
import FooterBtns from '@/components/footer-btns/index.vue'
/** Interface Start **/
type FormInstance = InstanceType<typeof ElForm>
const formRef = ref<FormInstance>();
/** Interface Start **/
/** Data Start **/
const form = ref<AppSettings_Req>({
app_id: '', // 开放平台appid
app_secret: '' // 开放平台appSecrets
})
// 表单验证
const formRules = reactive<object>({
app_id: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
app_secret: [{ required: true, message: '必填项不能为空', trigger: 'blur' }],
})
/** Data End **/
/** Methods Start **/
// 获取微信公众平台配置
const getAppSettings = async (): Promise<void> => {
form.value = await apiWechatPlatformGet()
}
// 编辑微信公众平台配置
const handleAppStoreEdit = async (): Promise<void> => {
await apiWechatPlatformSet({ ...form.value })
getAppSettings()
}
// 提交数据
const onSubmit = (formEl: FormInstance | undefined): void => {
if (!formEl) return
formEl.validate((valid): boolean | undefined=> {
if (!valid) return false
handleAppStoreEdit()
})
}
/** Methods End **/
/** LifeCycle Start **/
getAppSettings()
/** LifeCycle End **/
</script>
<style lang="scss" scoped>
.ls-input {
width: 280px;
}
.ls-card-alert {
border-radius: 8px;
background-color: #ffffff;
padding: 0 24px 24px 24px;
flex: 1;
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<div class="estate">
<el-card shadow="never">
<el-form class="ls-form" :model="formData" label-width="80px" size="small" inline>
<el-form-item label="广告位名称">
<el-input
placeholder="请输入广告位标题"
class="ls-input"
v-model="formData.username"
/>
</el-form-item>
<el-form-item label="广告位属性">
<el-select v-model="formData.house_type" placeholder="全部">
<el-option label="全部" value=""></el-option>
<el-option label="系统默认" :value="1"></el-option>
<el-option label="自定义" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<div class="m-l-20">
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</div>
</el-form-item>
</el-form>
</el-card>
<el-card class="m-t-15" shadow="never" v-loading="pager.loading">
<router-link to="/content/advertising/advertising_edit">
<el-button type="primary" size="small"> 新增广告位 </el-button>
</router-link>
<div>
<el-table class="m-t-20" :data="pager.lists">
<el-table-column label="ID" prop="id" min-width="30"></el-table-column>
<el-table-column label="广告位名称" prop="username"> </el-table-column>
<el-table-column label="广告位属性" prop="mobile"></el-table-column>
<el-table-column label="图片建议尺寸" prop="budget"> </el-table-column>
<el-table-column label="排序" prop="area"></el-table-column>
<el-table-column label="广告位状态" prop="plot"></el-table-column>
<el-table-column label="添加时间" prop="floor"> </el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template v-slot="{ row }">
<router-link
class="m-r-10"
:to="{
path: '',
query: {
id: row.id,
},
}"
>
<el-button type="text">编辑</el-button>
</router-link>
<popup class="m-r-10 inline">
<template #trigger>
<el-button type="text">删除</el-button>
</template>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex row-right">
<pagination
v-model="pager"
@change="requestApi"
layout="total, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { apiBuyHouseList } from '@/api/entrust'
import Pagination from '@/components/pagination/index.vue'
import Popup from '@/components/Popup/index.vue'
import { usePages } from '@/core/hooks/pages'
const formData = reactive({
username: '',
mobile: '',
house_type: '',
status: '',
start: '',
end: '',
})
const { pager, requestApi, resetParams, resetPage } = usePages({
callback: apiBuyHouseList,
params: formData,
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,101 @@
<template>
<div class="category">
<el-card class="m-t-15" shadow="never" v-loading="pager.loading">
<classify-form
@refresh="requestApi"
type="primary"
btnText="新增资讯分类"
></classify-form>
<div>
<el-table class="m-t-20" :data="pager.lists">
<el-table-column label="ID" prop="id" min-width="30"></el-table-column>
<el-table-column label="资讯分类名称" prop="name"> </el-table-column>
<el-table-column label="排序" prop="sort"></el-table-column>
<el-table-column label="状态" prop="is_show_desc"> </el-table-column>
<el-table-column label="添加时间" prop="create_time"> </el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<classify-form
:id="scope.row.id"
type="text"
btnText="编辑"
@refresh="requestApi"
></classify-form>
<popup
class="m-r-10 inline"
@confirm="handleStatusChange(scope.row.is_show, scope.row.id)"
>
<template #trigger>
<el-button type="text">
{{ scope.row.is_show ? '停用' : '启用' }}
</el-button>
</template>
{{
scope.row.is_show
? '确定要停用资讯分类吗?'
: '确定要启用资讯分类吗?'
}}
</popup>
<popup class="m-r-10 inline" @confirm="handleDelete(scope.row.id)">
<template #trigger>
<el-button type="text">删除</el-button>
</template>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex row-right">
<pagination
v-model="pager"
@change="requestApi"
layout="total, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import {
apiArticleCategoryList,
apiArticleCategoryDelete,
apiArticleCategoryStatus,
} from '@/api/information'
import { onMounted, reactive, ref } from 'vue'
import type { ElTable } from 'element-plus'
import Pagination from '@/components/pagination/index.vue'
import Popup from '@/components/Popup/index.vue'
import classifyForm from './components/classify-form.vue'
import { usePages } from '@/core/hooks/pages'
interface Lists {}
let tableData = ref<Lists[]>([])
const { pager, requestApi, resetParams, resetPage } = usePages({
callback: apiArticleCategoryList,
params: {},
})
// 删除分类
const handleDelete = async (id: number) => {
await apiArticleCategoryDelete({ id })
requestApi()
}
// 状态修改
const handleStatusChange = async (event: Event, id: number) => {
await apiArticleCategoryStatus({ is_show: event ? 0 : 1, id })
requestApi()
}
onMounted(() => {
requestApi()
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,154 @@
<template>
<popup
class="m-r-10 inline"
ref="popupRef"
:async="true"
:clickModalClose="false"
:title="btnText"
:center="true"
@close="handleClose(classifyFormRef)"
@open="handleOpen"
@confirm="onSubmit(classifyFormRef)"
width="40vw"
>
<template #trigger>
<el-button v-if="type === 'primary'" type="primary" size="small">
{{ btnText }}
</el-button>
<el-button v-if="type === 'text'" type="text" size="small">{{ btnText }}</el-button>
</template>
<el-form
:model="formData"
ref="classifyFormRef"
class="classify-form"
:rules="rules"
size="mini"
label-width="90px"
>
<el-form-item label="资讯分类:" prop="name">
<el-input class="ls-input" v-model="formData.name" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="排序:">
<el-input class="ls-input" v-model="formData.sort" placeholder="请输入"></el-input>
<div class="muted">默认排序为0数字越大排序越靠前</div>
</el-form-item>
<el-form-item label="状态:">
<el-switch
v-model="formData.is_show"
size="small"
:active-text="formData.is_show ? '启用' : '关闭'"
:active-value="1"
:inactive-value="0"
/>
</el-form-item>
</el-form>
</popup>
</template>
<script lang="ts" setup>
import {
apiArticleCategoryAdd,
apiArticleCategoryEdit,
apiArticleCategoryDetail,
} from '@/api/information'
import Popup from '@/components/popup/index.vue'
import { ref, reactive, withDefaults, defineEmits } from 'vue'
import type { ElForm } from 'element-plus'
interface classifyFormObj {
name?: string
sort?: number
is_show?: string | number
}
type FormInstance = InstanceType<typeof ElForm>
const classifyFormRef = ref<FormInstance>()
const props = withDefaults(
defineProps<{
type?: string
id?: object | any
btnText: string
}>(),
{
type: 'add',
id: '',
btnText: '',
}
)
const emit = defineEmits(['refresh'])
/** Data Start **/
const formData = ref<classifyFormObj>({
name: '',
sort: 0,
is_show: '',
})
// 表单校验规则
const rules = reactive({
name: [{ required: true, message: '请输入资讯分类', trigger: 'blur' }] as any[],
})
const popupRef = ref<any>(null)
/** Data End **/
/** Methods Start **/
// 弹窗关闭
const handleClose = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
}
// 弹窗打开时
const handleOpen = () => {
if (props.id) getClassifyDetail(props.id)
}
// 获取分类详情
const getClassifyDetail = async (id: number) => {
;(formData.value as {}) = await apiArticleCategoryDetail({ id })
}
// 添加分类
const addClassify = async () => {
await apiArticleCategoryAdd({ ...formData.value })
emit('refresh')
}
// 编辑分类
const editClassify = async () => {
await apiArticleCategoryEdit({ ...formData.value, id: props.id })
emit('refresh')
}
// 提交表单
const onSubmit = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (!valid) return false
if (!props.id) addClassify()
else editClassify()
popupRef.value.visible = false
})
}
/** Methods End **/
</script>
<style lang="scss">
.el-dialog__header {
text-align: left;
font-size: 16px;
color: #101010;
}
.ls-input {
width: 340px;
}
.classify-form {
margin-top: 30px;
margin-bottom: 100px;
}
</style>

View File

@@ -0,0 +1,156 @@
<template>
<div class="information-edit">
<el-card shadow="never">
<el-page-header content="新增资讯" @back="$router.back()" />
</el-card>
<el-card class="m-t-15" shadow="never">
<el-form
ref="formDataRefs"
class="ls-form"
:model="formData"
:rules="rules"
label-width="150px"
size="small"
>
<el-form-item label="资讯标题:" prop="title">
<el-input
class="ls-input"
type="textarea"
v-model="formData.title"
placeholder="多行输入"
:rows="6"
></el-input>
</el-form-item>
<el-form-item label="封面图:" prop="image">
<material-select v-model="formData.image" :limit="1"></material-select>
<div class="muted">建议尺寸240*180支持jpgpng格式</div>
</el-form-item>
<el-form-item label="所属分类:">
<el-select v-model="formData.cid" placeholder="请选择">
<el-option
v-for="(item, index) in informationClassify"
:key="index"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="状态:" prop="is_show">
<el-switch
v-model="formData.is_show"
size="small"
:active-text="formData.is_show ? '启用' : '关闭'"
:active-value="1"
:inactive-value="0"
/>
</el-form-item>
<el-form-item label="文章内容:" class="rich-text">
<editor v-model="formData.content" height="664"></editor>
</el-form-item>
</el-form>
</el-card>
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit(formDataRefs)">保存</el-button>
</footer-btns>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import type { ElForm } from 'element-plus'
import MaterialSelect from '@/components/material-select/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import Editor from '@/components/editor/index.vue'
import {
apiArticleAdd,
apiArticleEdit,
apiArticleDetail,
apiAllArticleCategory,
} from '@/api/information'
import { useAdmin } from '@/core/hooks/app'
interface formDataObj {
title: string
image: string
cid: number | string
is_show: number
content: string
}
// 所属分类
let informationClassify = ref<any[]>([])
type FormInstance = InstanceType<typeof ElForm>
const formDataRefs = ref<FormInstance>()
const { router, route } = useAdmin()
const id: any = route.query.id
// 表单数据
let formData = ref<formDataObj>({
title: '', // 资讯标题
image: '', // 封面图
cid: '', // 所属分类
is_show: 1, // 状态
content: '',
})
// 表单校验规则
const rules = reactive<any>({
title: [{ required: true, message: '请输入标题', trigger: ['blur'] }],
image: [{ required: true, message: '不能为空', trigger: ['blur'] }],
})
// 获取详情
const getInformationDetali = async (id: number) => {
;(formData.value as {}) = await apiArticleDetail({ id })
}
// 添加
const handleInformationAdd = async () => {
await apiArticleAdd({ ...formData.value })
router.back()
}
// 编辑
const handleInformationEdit = async () => {
await apiArticleEdit({ ...formData.value })
router.back()
}
// 获取所属分类
const getInformationClassify = async () => {
;(informationClassify.value as {}) = await apiAllArticleCategory()
}
// 提交数据
const onSubmit = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (!valid) return false
if (!id) handleInformationAdd()
else handleInformationEdit()
})
}
onMounted(() => {
if (id) getInformationDetali(id)
getInformationClassify()
})
</script>
<style lang="scss" scoped>
.ls-input {
width: 340px;
}
.rich-text {
width: 1000px;
}
</style>

View File

@@ -0,0 +1,151 @@
<template>
<div class="estate">
<el-card shadow="never">
<el-form class="ls-form" :model="formData" label-width="80px" size="small" inline>
<el-form-item label="资讯标题">
<el-input
placeholder="请输入资讯标题"
class="ls-input"
v-model="formData.title"
/>
</el-form-item>
<el-form-item label="资讯分类">
<el-select v-model="formData.cid" placeholder="请选择">
<el-option
v-for="(item, index) in informationClassify"
:key="index"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<div class="m-l-20">
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</div>
</el-form-item>
</el-form>
</el-card>
<el-card class="m-t-15" shadow="never" v-loading="pager.loading">
<router-link to="/content/information/information_edit">
<el-button type="primary" size="small"> 新增咨询 </el-button>
</router-link>
<div>
<el-table class="m-t-20" :data="pager.lists">
<el-table-column label="ID" prop="id" min-width="30"></el-table-column>
<el-table-column label="封面" prop="image">
<template #default="scope">
<div class="flex">
<el-image
:src="scope.row.image"
style="width: 70px; height: 70px"
></el-image>
</div>
</template>
</el-table-column>
<el-table-column label="资讯标题" prop="title"></el-table-column>
<el-table-column label="资讯分类" prop="cate_name"> </el-table-column>
<el-table-column label="浏览量" prop="visit"></el-table-column>
<el-table-column label="资讯状态" prop="is_show">
<template #default="scope">
<el-switch
v-model="scope.row.is_show"
:active-value="1"
:inactive-value="0"
@change="handleStatusChange($event, scope.row.id)"
/>
</template>
</el-table-column>
<el-table-column label="发布时间" prop="create_time"> </el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<router-link
class="m-r-10"
:to="{
path: '/content/information/information_edit',
query: {
id: scope.row.id,
},
}"
>
<el-button type="text">编辑</el-button>
</router-link>
<popup class="m-r-10 inline" @confirm="handleDelete(scope.row.id)">
<template #trigger>
<el-button type="text">删除</el-button>
</template>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex row-right">
<pagination
v-model="pager"
@change="requestApi"
layout="total, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import {
apiArticleList,
apiAllArticleCategory,
apiArticleDelete,
apiArticleStatus,
} from '@/api/information'
import { onMounted, reactive, ref } from 'vue'
import Pagination from '@/components/pagination/index.vue'
import Popup from '@/components/Popup/index.vue'
import { usePages } from '@/core/hooks/pages'
interface formDataObj {
title?: string
cid: string
}
// 资讯分类
let informationClassify = ref<any[]>([])
let formData = ref<formDataObj>({
title: '',
cid: '',
})
const { pager, requestApi, resetParams, resetPage } = usePages({
callback: apiArticleList,
params: formData.value,
})
// 获取资讯分类
const getInformationClassify = async () => {
;(informationClassify.value as {}) = await apiAllArticleCategory()
}
// 修改状态
const handleStatusChange = async (event: Event, id: number) => {
await apiArticleStatus({ is_show: event, id })
requestApi()
}
// 删除
const handleDelete = async (id: number) => {
await apiArticleDelete({ id })
requestApi()
}
onMounted(() => {
requestApi()
getInformationClassify()
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,169 @@
<template>
<div class="estate">
<el-card shadow="never">
<el-form class="ls-form" :model="formData" label-width="80px" size="small" inline>
<el-form-item label="广告名称">
<el-input
placeholder="请输入广告位标题"
class="ls-input"
v-model="formData.name"
/>
</el-form-item>
<el-form-item label="广告位置">
<el-select v-model="formData.pid" placeholder="全部">
<el-option
v-for="(item, index) in adPositionLists"
:key="index"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="广告状态">
<el-select v-model="formData.status" placeholder="全部">
<el-option label="全部" value=""></el-option>
<el-option label="启用" :value="1"></el-option>
<el-option label="停用" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<div class="m-l-20">
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</div>
</el-form-item>
</el-form>
</el-card>
<el-card class="m-t-15" shadow="never" v-loading="pager.loading">
<router-link to="/decoration/advertising_edit">
<el-button type="primary" size="small"> 新增广告 </el-button>
</router-link>
<div>
<el-table class="m-t-20" :data="pager.lists">
<el-table-column label="ID" prop="id" min-width="30"></el-table-column>
<el-table-column label="广告名称" prop="name"> </el-table-column>
<el-table-column label="广告位置" prop="ap_name"></el-table-column>
<el-table-column label="广告图片" prop="image">
<template #default="scope">
<div class="flex col-center">
<el-image
:src="scope.row.image"
style="width: 60px; height: 60px"
:fit="'cover'"
></el-image>
</div>
</template>
</el-table-column>
<el-table-column label="广告链接" prop="link_address_desc"></el-table-column>
<el-table-column label="排序" prop="sort"></el-table-column>
<el-table-column label="广告状态" prop="status">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="handleStatusChange($event, scope.row.id)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<router-link
class="m-r-10"
:to="{
path: '/decoration/advertising_edit',
query: {
id: scope.row.id,
},
}"
>
<el-button type="text">编辑</el-button>
</router-link>
<popup class="m-r-10 inline" @confirm="handleDelete(scope.row.id)">
<template #trigger>
<el-button type="text">删除</el-button>
</template>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex row-right">
<pagination
v-model="pager"
@change="requestApi"
layout="total, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { apiAdLists, apiAdDel, apiAdEditStatus, apiAdPositionLists } from '@/api/decoration'
import Pagination from '@/components/pagination/index.vue'
import Popup from '@/components/Popup/index.vue'
import { usePages } from '@/core/hooks/pages'
import { number } from 'echarts/core'
/** Interface Start **/
interface formDataObj {
name?: string
status?: string
pid: string
}
interface ListsObj {
name: string
index: number
id: number | string
}
/** Interface End **/
/** Data Start **/
let adPositionLists = ref<Array<ListsObj> | null>([]) //广告位列表
let formData = reactive<formDataObj>({
name: '',
status: '',
pid: '',
})
const { pager, requestApi, resetParams, resetPage } = usePages({
callback: apiAdLists,
params: formData,
})
/** Data End **/
/** Methods Start **/
//获取广告位列表
const getAdPositionLists = async (): Promise<void> => {
const res = await apiAdPositionLists()
;(adPositionLists.value as object) = (res as any).lists
}
// 删除广告位
const handleDelete = async (id: number): Promise<void> => {
await apiAdDel({ id })
requestApi()
}
// 状态修改
const handleStatusChange = async (event: Event, id: number): Promise<void> => {
await apiAdEditStatus({ status: event, id })
requestApi()
}
/** Methods End **/
onMounted(() => {
requestApi()
getAdPositionLists()
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,239 @@
<template>
<!-- Header Start -->
<el-card shadow="never">
<el-page-header :content="id ? '编辑广告' : '新增广告'" @back="$router.back()" />
</el-card>
<!-- Header End -->
<!-- Main Start -->
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="120px"
class="formData"
size="small"
>
<el-card shadow="never" style="margin-top: 15px">
<el-form-item label="广告名称:" prop="name">
<el-input class="ls-input" v-model="formData.name" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="广告位置:" prop="name">
<el-select v-model="formData.pid" class="ls-input" placeholder="请选择">
<el-option
v-for="(item, index) in adPositionLists"
:key="index"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="图标:">
<material-select v-model="formData.image" :limit="1"></material-select>
<div class="muted">
建议尺寸宽750*
{{ formData.pid == 2 ? '200' : '340' }}
像素的jpgjpegpnggif图片
</div>
</el-form-item>
<el-form-item label="链接地址:" prop="link_address">
<el-radio v-model="formData.link_type" name="1" :label="1">商城页面</el-radio>
<!-- <el-radio v-model="formData.link_type" name="2" :label="2">商品分类</el-radio> -->
<el-radio v-model="formData.link_type" name="3" :label="3">自定义链接</el-radio>
<div>
<!-- 商城页面 -->
<el-select
v-model="goodsLink"
class="ls-input"
v-if="formData.link_type === 1"
placeholder="请选择"
>
<el-option
v-for="(item, index) in showPageData"
:key="index"
:label="item.name"
:value="item.index + ''"
></el-option>
</el-select>
<!-- 自定义链接 -->
<el-input
class="ls-input"
v-if="formData.link_type === 3"
v-model="customLink"
placeholder="请输入"
></el-input>
</div>
</el-form-item>
<el-form-item label="排序:">
<el-input class="ls-input" v-model="formData.sort" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="状态:" prop="status">
<el-switch
v-model="formData.status"
size="small"
:active-text="formData.status ? '启用' : '关闭'"
:active-value="1"
:inactive-value="0"
/>
</el-form-item>
</el-card>
</el-form>
<!-- Main End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit(formRef)">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import {
apiAdAdd,
apiAdEdit,
apiAdDetail,
apiShowPage,
apiAdPositionLists,
} from '@/api/decoration'
import { ref, reactive, onMounted } from 'vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import MaterialSelect from '@/components/material-select/index.vue'
import type { ElForm } from 'element-plus'
import { useAdmin } from '@/core/hooks/app'
/** Interface Start **/
interface formDataObj {
name?: string //菜单名称
image?: string //菜单图片
pid: string | number //广告位
link_type?: number | string //链接类型1-商城页面2-商品分类3-自定义链接
link_address: string | number //链接地址
sort: number //排序
status: number //状态
}
interface ListsObj {
name: string
index: number
id: number | string
}
type FormInstance = InstanceType<typeof ElForm>
const formRef = ref<FormInstance>()
/** Interface End **/
/** Data Start **/
const { router, route } = useAdmin()
const id: any = route.query.id
let formData = ref<formDataObj>({
name: '',
image: '',
pid: '',
link_type: 1,
link_address: '',
sort: 0,
status: 1,
})
let adPositionLists = ref<Array<ListsObj> | null>([]) //广告位列表
let showPageData = ref<Array<ListsObj> | null>([]) //商品页面
let goodsCategoryPage = ref<Array<ListsObj> | null>([]) //分类页面数据
const goodsLink = ref<number | string>('') //link_type === 1 时商品页面的选择
const categoryLink = ref<number | string>('') //link_type === 2 时分类页面的选择
const customLink = ref<number | string>('') //自定义路径
// 表单娇艳规则
const rules = reactive<object>({
name: [{ required: true, message: '请输入菜单名称', trigger: 'blur' }],
link_address: [{ required: true, message: '链接地址不能为空', trigger: [] }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
})
/** Data End **/
/** Methods Start **/
// 获取详情
const getAdDetail = async (id: number): Promise<void> => {
(formData.value as object) = await apiAdDetail({ id })
switch (formData.value.link_type) {
case 1:
goodsLink.value = formData.value.link_address
break
case 2:
categoryLink.value = formData.value.link_address
break
case 3:
customLink.value = formData.value.link_address
break
}
}
//获取广告位列表
const getAdPositionLists = async (): Promise<void> => {
const res = await apiAdPositionLists()
;(adPositionLists.value as object) = (res as any).lists
}
// 获取商城页面
const getShowPage = async (): Promise<void> => {
;(showPageData.value as object) = await apiShowPage()
}
// 添加菜单
const handleAdAdd = async (): Promise<void> => {
await apiAdAdd({ ...formData.value })
router.back()
}
// 编辑菜单
const handleAdEdit = async (): Promise<void> => {
await apiAdEdit({ ...formData.value })
router.back()
}
// 提交数据
const onSubmit = (formEl: FormInstance | undefined): void => {
switch (formData.value.link_type) {
case 1:
formData.value.link_address = goodsLink.value
break
case 2:
formData.value.link_address = categoryLink.value
break
case 3:
formData.value.link_address = customLink.value
break
}
if (!formEl) return
formEl.validate((valid): boolean | undefined => {
if (!valid) return false
if (!id) handleAdAdd()
else handleAdEdit()
})
}
/** Methods End **/
/** LifeCycle Start **/
// 请求详情 => 如果是编辑的话
onMounted(() => {
if (id) getAdDetail(id)
getAdPositionLists()
getShowPage()
})
/** LifeCycle End **/
</script>
<style lang="scss">
.ls-input,
.select {
width: 340px;
}
</style>

View File

@@ -0,0 +1,165 @@
<template>
<popup
class="m-r-10 inline"
:clickModalClose="false"
title="编辑导航"
:center="true"
@close="handleClose(tabBarFormRef)"
@open="handleOpen"
@confirm="onSubmit(tabBarFormRef)"
width="50vw"
>
<!-- Trigger Start -->
<template #trigger>
<el-button v-if="type === 'primary'" type="primary" size="small">
{{ btnText }}
</el-button>
<el-button v-if="type === 'text'" type="text" size="small">{{ btnText }}</el-button>
</template>
<!-- Trigger End -->
<!-- Form Start -->
<el-form
:model="tabBarForm"
ref="tabBarFormRef"
class="tabbar-form"
:rules="rules"
size="mini"
label-width="150px"
>
<el-form-item label="导航名称" prop="name">
<el-input class="ls-input" v-model="tabBarForm.name" placeholder="请输入">
</el-input>
</el-form-item>
<el-form-item label="默认图表" prop="un_selected_icon">
<material-select v-model="tabBarForm.un_selected_icon" :limit="1"></material-select>
<div class="muted">建议尺寸宽88*高88像素的jpgjpegpnggif图片</div>
</el-form-item>
<el-form-item label="选中图表" prop="un_selected_icon">
<material-select v-model="tabBarForm.selected_icon" :limit="1"></material-select>
<div class="muted">建议尺寸宽88*高88像素的jpgjpegpnggif图片</div>
</el-form-item>
<el-form-item label="默认字体" prop="un_selected_icon">
<el-input
class="ls-input"
v-model="tabBarForm.un_selected_color"
placeholder="请输入"
>
<template #append>
<el-color-picker v-model="tabBarForm.un_selected_color" />
</template>
</el-input>
</el-form-item>
<el-form-item label="选中字体" prop="un_selected_icon">
<el-input class="ls-input" v-model="tabBarForm.selected_color" placeholder="请输入">
<template #append>
<el-color-picker v-model="tabBarForm.selected_color" />
</template>
</el-input>
</el-form-item>
</el-form>
<!-- Form End -->
</popup>
</template>
<script lang="ts" setup>
import { apiTabBarDetail, apiTabBarEdit } from '@/api/decoration'
import Popup from '@/components/popup/index.vue'
import { ref, reactive, withDefaults } from 'vue'
import type { ElForm } from 'element-plus'
import MaterialSelect from '@/components/material-select/index.vue'
interface tabBarFormObj {
name?: string //导航名称
selected_icon?: string //选中图标
un_selected_icon?: string //默认图标
selected_color?: string //选中颜色
un_selected_color?: string //默认颜色
}
type FormInstance = InstanceType<typeof ElForm>
const tabBarFormRef = ref<FormInstance>()
const props = withDefaults(
defineProps<{
type?: string
id?: object | any
btnText: string
}>(),
{
type: 'add',
id: '',
btnText: '',
}
)
/** Emit Start **/
const emit = defineEmits(['refresh'])
/** Emit End **/
/** Data Start **/
const tabBarForm = ref<tabBarFormObj>({
name: '',
selected_icon: '',
un_selected_icon: '',
selected_color: '',
un_selected_color: '',
})
// 表单娇艳规则
const rules = reactive({
name: [{ required: true, message: '请输入单位', trigger: 'blur' }] as any[],
})
/** Data End **/
/** Methods Start **/
// 弹窗关闭
const handleClose = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
}
// 弹窗打开时
const handleOpen = () => {
if (props.id) getTabBarDetail(props.id)
}
// 获取单位详情
const getTabBarDetail = async (id: number) => {
;(tabBarForm.value as {}) = await apiTabBarDetail({ id })
}
// 编辑单位
const tabBarEdit = async () => {
await apiTabBarEdit({ ...tabBarForm.value, id: props.id })
emit('refresh')
}
// 提交数据
const onSubmit = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (!valid) return false
tabBarEdit()
})
}
/** Methods End **/
</script>
<style lang="scss">
.el-dialog__header {
text-align: left;
font-size: 16px;
color: #101010;
}
.ls-input {
width: 340px;
}
.tabbar-form {
margin-top: 30px;
margin-bottom: 100px;
.el-input-group__append {
padding: 0;
border: 0;
}
}
</style>

View File

@@ -0,0 +1,111 @@
<template>
<div class="estate">
<el-card shadow="never">
<el-alert
title="* 设置移动端商城首页导航区的菜单,支持选择跳转商城页面、或者填写自定义链接"
type="info"
:closable="false"
></el-alert>
</el-card>
<el-card class="m-t-15" shadow="never" v-loading="pager.loading">
<router-link to="/decoration/home_edit">
<el-button type="primary" size="small"> 新增菜单 </el-button>
</router-link>
<el-table class="m-t-20" :data="pager.lists">
<el-table-column label="菜单图标" prop="image">
<template #default="scope">
<div class="flex">
<el-image
:src="scope.row.image"
style="width: 70px; height: 70px"
></el-image>
</div>
</template>
</el-table-column>
<el-table-column label="菜单名称" prop="name"></el-table-column>
<el-table-column label="链接地址" prop="link_address_desc"> </el-table-column>
<el-table-column label="排序" prop="sort"></el-table-column>
<el-table-column label="状态" prop="status_desc"> </el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<router-link
class="m-r-10"
:to="{
path: '/decoration/home_edit',
query: {
id: scope.row.id,
},
}"
>
<el-button type="text">编辑</el-button>
</router-link>
<popup
class="m-r-10 inline"
@confirm="handleStatusChange(scope.row.status, scope.row.id)"
>
<template #trigger>
<el-button type="text">
{{ scope.row.status ? '停用' : '启用' }}
</el-button>
</template>
{{
scope.row.status ? '确定要停用当前菜单吗?' : '确定要启用当前菜单吗?'
}}
</popup>
<popup class="m-r-10 inline" @confirm="handleDelete(scope.row.id)">
<template #trigger>
<el-button type="text">删除</el-button>
</template>
</popup>
</template>
</el-table-column>
</el-table>
<div class="flex row-right">
<pagination
v-model="pager"
@change="requestApi"
layout="total, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { apiHomeMenuLists, apiHomeMenuStatusEdit, apiHomeMenuDel } from '@/api/decoration'
import Pagination from '@/components/pagination/index.vue'
import Popup from '@/components/Popup/index.vue'
import { usePages } from '@/core/hooks/pages'
const { pager, requestApi } = usePages({
callback: apiHomeMenuLists,
})
// 删除菜单
const handleDelete = async (id: number): Promise<void> => {
await apiHomeMenuDel({ id })
requestApi()
}
// 状态修改
const handleStatusChange = async (event: Event, id: number): Promise<void> => {
await apiHomeMenuStatusEdit({ status: event ? 0 : 1, id })
requestApi()
}
onMounted(() => {
requestApi()
})
</script>
<style lang="scss" scoped>
.el-alert.el-alert--info {
background-color: $color-primary-light-9;
color: $color-primary;
}
</style>

View File

@@ -0,0 +1,218 @@
<template>
<!-- Header Start -->
<el-card shadow="never">
<el-page-header :content="id ? '编辑菜单' : '新增菜单'" @back="$router.back()" />
</el-card>
<!-- Header End -->
<!-- Main Start -->
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="120px"
class="formData"
size="small"
>
<el-card shadow="never" style="margin-top: 15px">
<el-form-item label="菜单名称:" prop="name">
<el-input class="ls-input" v-model="formData.name" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="图标:">
<material-select v-model="formData.image" :limit="1"></material-select>
<div class="muted">建议尺寸宽200*高200像素的jpgjpegpnggif图片</div>
</el-form-item>
<el-form-item label="链接地址:" prop="link_address">
<el-radio v-model="formData.link_type" name="1" :label="1">商城页面</el-radio>
<!-- <el-radio v-model="formData.link_type" name="2" :label="2">商品分类</el-radio> -->
<el-radio v-model="formData.link_type" name="3" :label="3">自定义链接</el-radio>
<div>
<!-- 商城页面 -->
<el-select
v-model="goodsLink"
class="ls-input"
v-if="formData.link_type === 1"
placeholder="请选择"
>
<el-option
v-for="(item, index) in showPageData"
:key="index"
:label="item.name"
:value="item.index"
></el-option>
</el-select>
<!-- 商城分类 -->
<el-select
v-model="categoryLink"
class="ls-input"
v-if="formData.link_type === 2"
placeholder="请选择"
>
<el-option
v-for="(item, index) in goodsCategoryPage"
:key="index"
:label="item.name"
:value="item.id + ''"
></el-option>
</el-select>
<!-- 自定义链接 -->
<el-input
class="ls-input"
v-if="formData.link_type === 3"
v-model="customLink"
placeholder="请输入"
></el-input>
</div>
</el-form-item>
<el-form-item label="排序:">
<el-input class="ls-input" v-model="formData.sort" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="状态:" prop="status">
<el-switch
v-model="formData.status"
size="small"
:active-text="formData.status ? '启用' : '关闭'"
:active-value="1"
:inactive-value="0"
/>
</el-form-item>
</el-card>
</el-form>
<!-- Main End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit(formRef)">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script setup lang="ts">
import {
apiShowPage,
apiHomeMenuAdd,
apiHomeMenuEdit,
apiHomeMenuDetail,
} from '@/api/decoration'
import { ref, reactive } from 'vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import MaterialSelect from '@/components/material-select/index.vue'
import type { ElForm } from 'element-plus'
import { useAdmin } from '@/core/hooks/app'
/* Interface Start */
interface formDataObj {
name?: string //菜单名称
image?: string //菜单图片
link_type?: number | string //链接类型1-商城页面2-商品分类3-自定义链接
link_address: string | number //链接地址
sort: number //排序
status: number //状态
}
interface Lists {
name: string
index: number
id: number | string
}
type FormInstance = InstanceType<typeof ElForm>
const formRef = ref<FormInstance>()
/* Interface End */
/* Data Start */
const { router, route } = useAdmin()
const id: any = route.query.id
let formData = ref<formDataObj>({
name: '',
image: '',
link_type: 1,
link_address: '',
sort: 0,
status: 1,
})
let showPageData = ref<Array<Lists> | null>([]) //商品页面
let goodsCategoryPage = ref<Array<Lists> | null>([]) //分类页面数据
const goodsLink = ref<number | string>('') //link_type === 1 时商品页面的选择
const categoryLink = ref<number | string>('') //link_type === 2 时分类页面的选择
const customLink = ref<number | string>('') //自定义路径
// 表单娇艳规则
const rules = reactive<object>({
name: [{ required: true, message: '请输入菜单名称', trigger: 'blur' }],
link_address: [{ required: true, message: '链接地址不能为空', trigger: [] }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
})
/* Data End */
/* Methods Start */
// 获取详情
const getHomeMenuDetail = async (id: number): Promise<void> => {
(formData.value as object) = await apiHomeMenuDetail({ id })
switch (formData.value.link_type) {
case 1:
goodsLink.value = formData.value.link_address
break
case 2:
categoryLink.value = formData.value.link_address
break
case 3:
customLink.value = formData.value.link_address
break
}
}
// 获取商城页面
const getShowPage = async (): Promise<void> => {
(showPageData.value as object) = await apiShowPage()
}
// 添加菜单
const handleHomeMenuAdd = async (): Promise<void> => {
await apiHomeMenuAdd({ ...formData.value })
router.back()
}
// 编辑菜单
const handleHomeMenuEdit = async (): Promise<void> => {
await apiHomeMenuEdit({ ...formData.value })
router.back()
}
// 提交数据
const onSubmit = (formEl: FormInstance | undefined): void => {
switch (formData.value.link_type) {
case 1:
formData.value.link_address = goodsLink.value
break
case 2:
formData.value.link_address = categoryLink.value
break
case 3:
formData.value.link_address = customLink.value
break
}
if (!formEl) return
formEl.validate((valid): boolean | undefined => {
if (!valid) return false
if (!id) handleHomeMenuAdd()
else handleHomeMenuEdit()
})
}
/* Methods End */
/* LifeCycle Start */
// 请求详情 => 如果是编辑的话
if (id) getHomeMenuDetail(id)
getShowPage()
/* LifeCycle End */
</script>
<style lang="scss" scoped>
.ls-input,
.select {
width: 340px;
}
</style>

View File

@@ -0,0 +1,81 @@
<template>
<div class="estate">
<el-card shadow="never">
<el-alert
title="* 编辑移动端商城底部标签栏的图标和名称"
type="info"
:closable="false"
></el-alert>
</el-card>
<el-card class="m-t-15" shadow="never" v-loading="pager.loading">
<el-table :data="pager.lists">
<el-table-column label="导航名称" prop="name"></el-table-column>
<el-table-column label="默认图标" prop="un_selected_icon">
<template #default="scope">
<div class="flex">
<el-image
:src="scope.row.un_selected_icon"
style="width: 70px; height: 70px"
></el-image>
</div>
</template>
</el-table-column>
<el-table-column label="选中图标" prop="selected_icon">
<template #default="scope">
<div class="flex">
<el-image
:src="scope.row.selected_icon"
style="width: 70px; height: 70px"
></el-image>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<tabbar-edit
:id="scope.row.id"
type="text"
btnText="编辑"
@refresh="requestApi"
></tabbar-edit>
</template>
</el-table-column>
</el-table>
<div class="flex row-right">
<pagination
v-model="pager"
@change="requestApi"
layout="total, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import { apiTabBarLists } from '@/api/decoration'
import Pagination from '@/components/pagination/index.vue'
import tabbarEdit from './components/tabbar-edit.vue'
import { usePages } from '@/core/hooks/pages'
const { pager, requestApi } = usePages({
callback: apiTabBarLists,
})
onMounted(() => {
requestApi()
})
</script>
<style lang="scss" scoped>
.el-alert.el-alert--info {
background-color: $color-primary-light-9;
color: $color-primary;
}
</style>

View File

@@ -0,0 +1,17 @@
<template>
<div class="error404">
<error :code="404" title="哎呀,出错了!您访问的页面不存在…"></error>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import Error from './components/error.vue'
export default defineComponent({
components: {
Error
},
setup() {}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,17 @@
<template>
<div class="error500">
<error :code="500" title="哎呀,出错了!系统错误,请稍后再试…"></error>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import Error from './components/error.vue'
export default defineComponent({
components: {
Error
},
setup() {}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,60 @@
<template>
<div class="error">
<div>
<div class="error-code">{{ code }}</div>
<div class="lg lighter m-t-30 m-b-30">
{{ title }}
</div>
<el-button type="primary" size="small" @click="$router.go(-1)">
{{ second }} 秒后返回上一页
</el-button>
</div>
</div>
</template>
<script lang="ts">
import { useAdmin } from '@/core/hooks/app'
import { defineComponent, onUnmounted, ref } from 'vue'
export default defineComponent({
props: {
code: Number,
title: String
},
setup() {
let timer: any = null
const second = ref(5)
const { router } = useAdmin()
timer = setInterval(() => {
if (second.value === 0) {
clearInterval(timer)
router.go(-1)
} else {
second.value--
}
}, 1000)
onUnmounted(() => {
clearInterval(timer)
})
return {
second
}
}
})
</script>
<style lang="scss" scoped>
.error {
text-align: center;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
.error-code {
color: $color-primary;
font-size: 150px;
}
.el-button {
width: 176px;
}
}
</style>

View File

@@ -0,0 +1,209 @@
<template>
<div class="admin">
<el-card shadow="never">
<el-page-header :content="id ? '编辑管理员' : '新增管理员'" @back="$router.back()" />
</el-card>
<el-card v-loading="loading" shadow="never" class="m-t-15">
<el-form
ref="formRefs"
:rules="rules"
class="ls-form"
:model="formData"
label-width="150px"
size="small"
>
<!-- 账号输入框 -->
<el-form-item label="账号:" prop="account">
<el-input v-model="formData.account" placeholder="请输入账号"></el-input>
</el-form-item>
<!-- 管理员头像 -->
<el-form-item label="头像:">
<material-select v-model="formData.avatar" :limit="1"></material-select>
<div class="muted">建议尺寸100*100px支持jpgjpegpng格式</div>
</el-form-item>
<!-- 名称输入框 -->
<el-form-item label="名称:" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称"></el-input>
</el-form-item>
<!-- 角色选择框 -->
<el-form-item label="角色:" prop="role_id">
<el-select v-model="formData.role_id" placeholder="请选择角色">
<el-option
v-for="(item, index) in roleList"
:key="index"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item label="密码:" prop="password">
<el-input
v-model="formData.password"
show-password
placeholder="请输入密码"
></el-input>
</el-form-item>
<!-- 确认密码输入框 -->
<el-form-item label="确认密码:" prop="password_confirm">
<el-input
v-model="formData.password_confirm"
show-password
placeholder="请输入确认密码"
></el-input>
</el-form-item>
<!-- 管理员状态 -->
<el-form-item label="管理员状态">
<el-switch v-model="formData.disable" :active-value="0" :inactive-value="1" />
</el-form-item>
<!-- 多处登录 -->
<el-form-item label="支持多处登录">
<el-switch
v-model="formData.multipoint_login"
:active-value="1"
:inactive-value="0"
/>
</el-form-item>
</el-form>
</el-card>
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit">保存</el-button>
</footer-btns>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, reactive, Ref, ref, toRefs } from 'vue'
import MaterialSelect from '@/components/material-select/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import { apiRoleLists, apiAdminDetail, apiAdminAdd, apiAdminEdit } from '@/api/auth'
import { ElForm } from 'element-plus'
import { useAdmin } from '@/core/hooks/app'
export default defineComponent({
components: {
MaterialSelect,
FooterBtns
},
setup() {
const formRefs: Ref<typeof ElForm | null> = ref(null)
const { router, route } = useAdmin()
const id = computed(() => route.query?.id)
const loading = ref(false)
// 表单数据
const roleList: Ref<any[]> = ref([])
const { formData, rules } = toRefs(
reactive({
formData: {
account: '',
name: '',
role_id: '',
avatar: '',
password: '',
password_confirm: '',
disable: 0,
multipoint_login: 0
},
rules: {
account: [
{
required: true,
message: '请输入账号',
trigger: ['blur']
}
],
name: [
{
required: true,
message: '请输入名称',
trigger: ['blur']
}
],
role_id: [
{
required: true,
message: '请选择角色',
trigger: ['blur']
}
],
password: [] as any[],
password_confirm: [] as any[]
}
})
)
const getRoleList = () => {
apiRoleLists({
page_type: 1
}).then((res: any) => {
roleList.value = res.lists
})
}
const getAdminDetail = () => {
if (!id.value) {
rules.value.password = [
{
required: true,
message: '请输入密码',
trigger: ['blur']
}
]
rules.value.password_confirm = [
{
required: true,
message: '请输入确认密码',
trigger: ['blur']
}
]
return
}
loading.value = true
apiAdminDetail({
id: id.value
})
.then((res: any) => {
formData.value = res
})
.finally(() => {
loading.value = false
})
}
const onSubmit = () => {
formRefs.value?.validate((valid: boolean) => {
if (!valid) {
return
}
const promise = id.value
? apiAdminEdit({ ...formData.value, id: id.value })
: apiAdminAdd(formData.value)
promise.then(() => {
setTimeout(() => router.go(-1), 500)
})
})
}
onMounted(() => {
getAdminDetail()
getRoleList()
})
return {
id,
formRefs,
loading,
formData,
rules,
roleList,
onSubmit
}
}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,178 @@
<template>
<div class="admin">
<el-card shadow="never">
<el-form class="ls-form" :model="formData" label-width="80px" size="small" inline>
<el-form-item label="账号:">
<el-input v-model="formData.account" class="ls-input" />
</el-form-item>
<el-form-item label="名称:">
<el-input v-model="formData.name" class="ls-input" />
</el-form-item>
<el-form-item label="角色:">
<el-select v-model="formData.role_id" placeholder="全部">
<el-option
v-for="(item, index) in roleList"
:key="index"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<div class="m-l-20">
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</div>
</el-form-item>
</el-form>
</el-card>
<el-card v-loading="pager.loading" class="m-t-15" shadow="never">
<router-link to="/permission/admin/edit">
<el-button type="primary" size="small">新增管理员</el-button>
</router-link>
<div class="m-t-15">
<el-table :data="pager.lists">
<el-table-column label="ID" prop="id" min-width="60"></el-table-column>
<el-table-column label="头像" min-width="100">
<template #default="{ row }">
<el-avatar :size="50" :src="row.avatar"></el-avatar>
</template>
</el-table-column>
<el-table-column label="账号" prop="account" min-width="100"></el-table-column>
<el-table-column label="名称" prop="name" min-width="100"></el-table-column>
<el-table-column
label="角色"
prop="role_name"
min-width="100"
></el-table-column>
<el-table-column
label="创建时间"
prop="create_time"
min-width="150"
></el-table-column>
<el-table-column
label="最近登录时间"
prop="login_time"
min-width="150"
></el-table-column>
<el-table-column
label="最近登录IP"
prop="login_ip"
min-width="100"
></el-table-column>
<el-table-column label="状态" min-width="100">
<template #default="{ row }">
<el-switch
v-model="row.disable"
:active-value="0"
:inactive-value="1"
@change="changeStatus(row)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<router-link
class="m-r-10"
:to="{
path: '/permission/admin/edit',
query: {
id: row.id
}
}"
>
<el-button type="text">编辑</el-button>
</router-link>
<popup class="m-r-10 inline" @confirm="handleDelete(row.id)">
<template #trigger>
<el-button type="text">删除</el-button>
</template>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex row-right">
<pagination
v-model="pager"
layout="total, prev, pager, next, jumper"
@change="requestApi"
/>
</div>
</el-card>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, reactive, Ref, ref } from 'vue'
import Pagination from '@/components/pagination/index.vue'
import Popup from '@/components/popup/index.vue'
import { apiAdminEdit, adminLists, apiAdminDelete, apiRoleLists } from '@/api/auth'
import { usePages } from '@/core/hooks/pages'
export default defineComponent({
components: {
Pagination,
Popup
},
setup() {
// 表单数据
const formData = reactive({
account: '',
name: '',
role_id: ''
})
const roleList: Ref<any[]> = ref([])
const { pager, requestApi, resetParams, resetPage } = usePages({
callback: adminLists,
params: formData
})
const changeStatus = (data: any) => {
apiAdminEdit({
id: data.id,
account: data.account,
name: data.name,
role_id: data.role_id,
disable: data.disable,
multipoint_login: data.multipoint_login
}).finally(() => {
requestApi()
})
}
const handleDelete = (id: number) => {
apiAdminDelete({ id }).then(() => {
requestApi()
})
}
const getRoleList = () => {
apiRoleLists({
page_type: 1
}).then((res: any) => {
roleList.value = res.lists
})
}
onMounted(() => {
requestApi()
getRoleList()
})
return {
formData,
roleList,
pager,
requestApi,
resetParams,
resetPage,
adminLists,
changeStatus,
handleDelete
}
}
})
</script>
<style lang="scss" scoped>
.ls-form {
margin-bottom: -16px;
}
</style>

View File

@@ -0,0 +1,250 @@
<template>
<div class="role-edit">
<el-card shadow="never">
<el-page-header :content="id ? '编辑角色' : '新增角色'" @back="$router.back()" />
</el-card>
<el-card shadow="never" class="m-t-15">
<el-form
ref="formRef"
:rules="rules"
class="ls-form"
:model="formData"
label-width="150px"
size="small"
>
<!-- 名称 -->
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称"></el-input>
</el-form-item>
<!-- 备注 -->
<el-form-item label="备注" prop="desc">
<el-input
v-model="formData.desc"
placeholder="请输入备注"
type="textarea"
:autosize="{ minRows: 4, maxRows: 6 }"
></el-input>
</el-form-item>
<!-- 权限 -->
<el-form-item label="权限" prop="auth_keys">
<div class="flex" style="margin-top: 3px">
<el-button type="text" size="mini" @click="allSelect()"> 全选 </el-button>
<el-button type="text" size="mini" @click="close()"> 全不选 </el-button>
</div>
<el-tree
ref="treeRef"
:data="menu.permissionsTree"
node-key="auth_key"
default-expand-all
icon="ArrowRight"
:props="{
children: 'sons',
label: 'name'
}"
empty-text=""
show-checkbox
@check-change="handlePermissionsCheckChange"
/>
</el-form-item>
</el-form>
</el-card>
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit">保存</el-button>
</footer-btns>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, Ref, ref, toRefs } from 'vue'
import {
apiConfigGetMenu,
apiConfigGetAuth,
apiRoleAdd,
apiRoleEdit,
apiRoleDetail
} from '@/api/auth'
import { ElInput, ElForm } from 'element-plus'
import FooterBtns from '@/components/footer-btns/index.vue'
import { useAdmin } from '@/core/hooks/app'
import { flatten } from '@/utils/util'
export default defineComponent({
components: {
FooterBtns
},
setup() {
const { route, router } = useAdmin()
const formRef: Ref<typeof ElForm | null> = ref(null)
const treeRef: Ref<typeof ElForm | null> = ref(null)
const id: Ref<Number | null> = ref(null) // 当前编辑角色ID
// 表单数据
const { formData, menu } = toRefs(
reactive({
formData: {
name: '', // 角色名称
desc: '', // 备注
auth_keys: [] // 权限
},
menu: {
permissionsTree: [], // 菜单
allAuthKeys: [] // 菜单全部的auth_keys
}
})
)
// 表单验证
const rules = {
name: [
{
required: true,
message: '请输入角色名称',
trigger: ['blur']
}
]
}
// 确定提交
const onSubmit = () => {
formRef.value?.validate((valid: boolean) => {
if (!valid) {
return
}
id.value ? roleEdit() : roleAdd()
})
}
const getMenu = () => {
apiConfigGetMenu()
.then((res: any) => {
// 获取菜单
menu.value.permissionsTree = res
// console.log('res', res)
// 数组扁平化
const menuFlatten = flatten(res, [], 'sons')
// console.log(menuFlatten)
// 获取菜单全部 auth_key
menu.value.allAuthKeys = menuFlatten.map((item: any) => item.auth_key) as never
})
.catch((err: any) => {
console.log('err', err)
})
}
// 添加角色
const roleAdd = () => {
apiRoleAdd({ ...formData.value })
.then((res: any) => {
console.log('res', res)
setTimeout(() => {
router.back()
}, 500)
})
.catch((err: any) => {
console.log('err', err)
})
}
// 编辑角色
const roleEdit = () => {
apiRoleEdit({
...formData.value,
id: id.value
})
.then((res: any) => {
console.log('res', res)
setTimeout(() => {
router.back()
}, 500)
})
.catch((err: any) => {
console.log('err', err)
})
}
// 角色详情
const roleDetail = () => {
apiRoleDetail({
id: id.value
})
.then((res: any) => {
console.log('res', res)
formData.value = res
// formData.value.auth_keys = []
treeRef.value?.setCheckedKeys(res.auth_keys)
})
.catch((err: any) => {
console.log('err', err)
})
}
// 权限树触发函数
const handlePermissionsCheckChange = (data: any, checked: boolean) => {
console.log(data)
if (!data.auth_key) {
return
}
const index = formData.value.auth_keys.findIndex(item => item == data.auth_key)
if (checked) {
index == -1 && (formData.value.auth_keys as any).push(data.auth_key)
return
}
if (index != -1) {
formData.value.auth_keys.splice(index, 1)
}
}
// 全选
const allSelect = () => {
treeRef.value?.setCheckedKeys(menu.value.allAuthKeys)
}
// 全不选
const close = () => {
treeRef.value?.setCheckedKeys([])
}
onMounted(() => {
const query: any = route.query
if (query.id) {
id.value = query.id * 1
roleDetail()
}
getMenu()
})
return {
id,
formData,
menu,
formRef,
treeRef,
rules,
getMenu,
roleAdd,
roleEdit,
roleDetail,
onSubmit,
handlePermissionsCheckChange,
close,
allSelect
}
}
})
</script>
<style lang="scss" scoped>
.role-edit {
:deep .el-textarea {
width: 340px;
}
}
</style>

View File

@@ -0,0 +1,106 @@
<template>
<div class="role">
<el-card shadow="never">
<router-link to="/permission/role/edit">
<el-button type="primary" size="small">新增角色</el-button>
</router-link>
<div v-loading="pager.loading" class="m-t-15">
<div class="m-t-15">
<el-table :data="pager.lists" size="medium">
<el-table-column prop="id" label="ID"></el-table-column>
<el-table-column prop="name" label="名称"></el-table-column>
<el-table-column prop="desc" label="备注"></el-table-column>
<el-table-column prop label="权限"></el-table-column>
<el-table-column prop="create_time" label="创建时间"></el-table-column>
<el-table-column prop label="操作">
<template #default="{ row }">
<!-- 编辑 -->
<router-link
class="m-r-10"
:to="{
path: '/permission/role/edit',
query: {
id: row.id
}
}"
>
<el-button type="text" size="mini">编辑</el-button>
</router-link>
<!-- 删除 -->
<popup class="m-r-10 inline" @confirm="handleDelete(row.id)">
<template #trigger>
<el-button type="text" size="mini">删除</el-button>
</template>
</popup>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex row-right">
<pagination
v-model="pager"
layout="total, prev, pager, next, jumper"
@change="requestApi"
/>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted } from 'vue'
import { apiRoleLists, apiRoleDel } from '@/api/auth'
import Pagination from '@/components/pagination/index.vue'
import Popup from '@/components/popup/index.vue'
import { usePages } from '@/core/hooks/pages'
export default defineComponent({
components: {
Pagination,
Popup
},
setup() {
// 表单数据
const formData = reactive({
id: 0, // 角色id
name: '', // 角色名称
desc: '', // 备注
create_time: '', // 创建时间
num: 0 // 使用该角色的人数
})
const { pager, requestApi } = usePages({
callback: apiRoleLists,
params: formData
})
// 删除角色
const handleDelete = (id: number) => {
apiRoleDel({
id
})
.then((res: any) => {
console.log('res', res)
requestApi()
})
.catch((err: any) => {
console.log('err', err)
})
}
onMounted(() => {
requestApi()
})
return {
formData,
apiRoleLists,
handleDelete,
pager,
requestApi
}
}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,106 @@
<template>
<div class="admin">
<el-card shadow="never">
<el-alert
title="* 设置商城客服联系信息;小程序客服在微信小程序后台开通"
type="info"
:closable="false"
></el-alert>
</el-card>
<el-card class="m-t-15" shadow="never">
<el-form
ref="formRefs"
class="ls-form"
:model="formData"
:rules="rules"
label-width="150px"
size="small"
>
<el-form-item label="客服二维码:" prop="qr_code">
<material-select v-model="formData.qr_code" :limit="1"></material-select>
<div class="muted">建议尺寸240*200支持jpgpng格式</div>
</el-form-item>
<el-form-item label="客服微信号:" prop="wechat">
<el-input v-model="formData.wechat" placeholder="请输入微信号"></el-input>
</el-form-item>
<el-form-item label="客服电话:" prop="phone">
<el-input v-model="formData.phone" placeholder="请输入电话"></el-input>
</el-form-item>
<el-form-item label="服务时间:" prop="service_time">
<el-input v-model="formData.service_time" placeholder=""></el-input>
<div class="muted">服务时间例如周一至周六9:00-19:00</div>
</el-form-item>
</el-form>
</el-card>
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit(formRefs)">保存</el-button>
</footer-btns>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import type { ElForm } from 'element-plus'
import MaterialSelect from '@/components/material-select/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import { apiCustomerServiceGetConfig, apiCustomerServiceSetConfig } from '@/api/setting'
type FormInstance = InstanceType<typeof ElForm>
const formRefs = ref<FormInstance>()
// 表单数据
const formData = ref({
qr_code: '', // 客服二维码
wechat: '', // 客服微信号
phone: '', // 客服手机号
service_time: '', // 服务时间
})
// 表单校验规则
const rules = reactive<object>({
qr_code: [
{
required: true,
message: '客服二维码不能为空',
trigger: ['blur'],
},
],
})
// 获取客服设置
const getCustomerServiceConfig = async (): Promise<void> => {
formData.value = await apiCustomerServiceGetConfig()
}
// 设置客服设置
const setCustomerServiceConfig = async ():Promise<void> => {
await apiCustomerServiceSetConfig({...formData.value})
getCustomerServiceConfig()
}
// 提交数据
const onSubmit = (formEl: FormInstance | undefined): void => {
if(!formEl) return
formEl.validate((valid): Boolean | undefined => {
if(!valid) return false
setCustomerServiceConfig()
})
}
getCustomerServiceConfig()
</script>
<style lang="scss" scoped>
.el-alert.el-alert--info {
background-color: $color-primary-light-9;
color: $color-primary;
}
</style>

View File

@@ -0,0 +1,90 @@
<!-- 系统环境 -->
<template>
<div class="system-environment">
<el-card shadow="never">
<div>服务器信息</div>
<div class="m-t-15">
<el-table :data="info.server" size="medium">
<el-table-column prop="param" label="参数"> </el-table-column>
<el-table-column prop="value" label="值"> </el-table-column>
</el-table>
</div>
</el-card>
<el-card shadow="never" class="m-t-15">
<div>PHP环境要求</div>
<div class="m-t-15">
<el-table :data="info.env" size="medium">
<el-table-column prop="option" label="选项"> </el-table-column>
<el-table-column prop="require" label="要求"> </el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-icon v-show="scope.row.status" color="#67C23A">
<Select />
</el-icon>
<el-icon v-show="!scope.row.status" color="#DB2828">
<close-bold />
</el-icon>
</template>
</el-table-column>
<el-table-column prop="remark" label="说明及帮助"> </el-table-column>
</el-table>
</div>
</el-card>
<el-card shadow="never" class="m-t-15">
<div>目录权限</div>
<div class="m-t-15">
<el-table :data="info.auth" size="medium">
<el-table-column prop="dir" label="选项"> </el-table-column>
<el-table-column prop="require" label="要求"> </el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-icon v-show="scope.row.status" color="#67C23A">
<Select />
</el-icon>
<el-icon v-show="!scope.row.status" color="#DB2828">
<close-bold />
</el-icon>
</template>
</el-table-column>
<el-table-column prop="remark" label="说明及帮助"> </el-table-column>
</el-table>
</div>
</el-card>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted } from 'vue'
import { apiSystemInfo } from '@/api/setting'
export default defineComponent({
setup() {
const info = reactive({
server: [], // 服务器信息
env: [], // PHP环境要求
auth: [] // 目录权限
})
const getSystemInfo = () => {
apiSystemInfo().then((res: any) => {
console.log('res', res)
info.server = res.server
info.env = res.env
info.auth = res.auth
})
}
onMounted(() => {
getSystemInfo()
})
return {
info,
getSystemInfo
}
}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,59 @@
<!-- 用户设置 -->
<template>
<!-- 用户mortar Start -->
<el-card shadow="never" class="m-t-16">
<template #header>
<span> 基本设置 </span>
</template>
<el-form ref="formRef" :model="formData" label-width="100px" size="small">
<el-form-item label="用户默认头像" prop="default_avatar">
<material-select v-model="formData.default_avatar" :limit="1"></material-select>
<div class="muted">建议尺寸400*400支持jpgpng格式</div>
</el-form-item>
</el-form>
</el-card>
<!-- 用户mortar End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { apiUserConfigGet, apiUserConfigSet } from '@/api/setting'
import materialSelect from '@/components/material-select/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
/** Data Start **/
const formData = ref<any>({
default_avatar: '', // string 客服二维码
wechat: '', // string 客服微信号
})
/** Data End **/
/** Methods Start **/
// 获取用户设置
const getUserConfig = async (): Promise<void> => {
formData.value = await apiUserConfigGet()
}
// 编辑用户设置
const handleUserConfigEdit = async (): Promise<void> => {
await apiUserConfigSet({ ...formData.value })
getUserConfig()
}
// 提交数据
const onSubmit = () => {
handleUserConfigEdit()
}
/** Methods End **/
/** LifeCycle Start **/
getUserConfig()
/** LifeCycle End **/
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,237 @@
<!-- 用户设置 -->
<template>
<div class="login-register">
<el-form ref="formRef" :model="formData" label-width="160px" size="small">
<!-- 通用设置 Start -->
<el-card shadow="never" class="m-t-15">
<template #header>
<span>通用设置</span>
</template>
<el-form-item label="注册方式" required>
<el-checkbox-group v-model="formData.register_way">
<el-checkbox :label="1">手机号码注册</el-checkbox>
</el-checkbox-group>
<div class="muted xs">
系统通用的注册用户方式至少勾选一项微信等渠道有其他授权注册方式请合理设置
</div>
</el-form-item>
<el-form-item label="手机号码注册需验证码" required>
<!-- switch开关 -->
<div class="flex col-center">
<el-switch v-model="formData.is_mobile_register_code" :active-value="1" :inactive-value="0" />
<span class="m-l-16">
{{ formData.is_mobile_register_code ? '开启' : '关闭' }}
</span>
</div>
<div class="muted xs">
1用户通过手机号码注册帐号时是否需要短信验证码验证填写的手机号码默认关闭
</div>
<div class="muted xs">2请开启对应短信验证码功能</div>
</el-form-item>
<el-form-item label="强制绑定手机">
<!-- switch开关 -->
<div class="flex col-center">
<el-switch v-model="formData.coerce_mobile" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{ formData.coerce_mobile ? '开启' : '关闭' }}</span>
</div>
<div class="muted xs">
用户无论通过何种方式注册用户时是否需要强制绑定手机绑定手机需要短信验证请开启对应短信验证码功能
</div>
</el-form-item>
<el-form-item label="登录方式" required>
<!-- 多选框 -->
<el-checkbox-group v-model="formData.login_way">
<el-checkbox :label="1">手机号密码登录</el-checkbox>
<el-checkbox :label="2">手机号验证码登录</el-checkbox>
</el-checkbox-group>
<div class="muted xs">
系统通用的登录方式至少勾选一项需要和通用注册方式匹配
微信等渠道有其他授权注册方式请合理设置
</div>
</el-form-item>
</el-card>
<!-- 通用设置 End -->
<!-- 微信公众号 Start -->
<el-card shadow="never" class="m-t-15">
<template #header>
<span>微信公众号</span>
</template>
<el-form-item label="微信公众号">
<a class="muted xs item-a" href="https://open.weixin.qq.com/">前往微信开放平台</a>
<div class="muted xs">
1商城在各渠道使用微信授权登录时强烈建议配置微信开放平台
</div>
<div class="muted xs">
2微信开放平台关联公众号小程序和APP后可实现各端用户账号统一识别买家唯一微信身份
</div>
<div class="muted xs">
3没有配置微信开放平台同一微信号会生成多个用户配置微信开放平台后已生成的用户账号无法合并
</div>
</el-form-item>
</el-card>
<!-- 微信公众号 End -->
<!-- 微信开放平台 Start -->
<el-card shadow="never" class="m-t-15">
<template #header>
<span>微信开放平台</span>
</template>
<el-form-item label="微信授权登录">
<!-- switch开关 -->
<div class="flex col-center">
<el-switch v-model="formData.h5_wechat_auth" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{ formData.h5_wechat_auth ? '开启' : '关闭' }}</span>
</div>
<div class="muted xs">
微信公众号是否支持微信授权登录方式关闭时使用注册设置里面的配置方法完成用户注册和登录
</div>
</el-form-item>
<el-form-item label="自动微信授权登录">
<div class="flex col-center">
<el-switch v-model="formData.h5_auto_wechat_auth" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{ formData.h5_auto_wechat_auth ? '开启' : '关闭' }}</span>
</div>
<div class="muted xs">
微信授权登录开启后可以勾选是否需要自动授权登录默认开启
</div>
</el-form-item>
</el-card>
<!-- 微信开放平台 End -->
<!-- 微信小程序 Start -->
<el-card shadow="never" class="m-t-15">
<template #header>
<span>微信小程序</span>
</template>
<el-form-item label="微信授权登录">
<!-- switch开关 -->
<div class="flex col-center">
<el-switch v-model="formData.mnp_wechat_auth" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{ formData.mnp_wechat_auth ? '开启' : '关闭' }}</span>
</div>
<div class="muted xs">
微信小程序是否支持微信授权登录方式关闭时使用注册设置里面的配置方法完成用户注册和登录
</div>
</el-form-item>
<el-form-item label="自动微信授权登录">
<div class="flex col-center">
<el-switch v-model="formData.mnp_auto_wechat_auth" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{
formData.mnp_auto_wechat_auth ? '开启' : '关闭'
}}</span>
</div>
<div class="muted xs">
微信授权登录开启后可以勾选是否需要自动授权登录默认开启
</div>
</el-form-item>
</el-card>
<!-- 微信小程序 End -->
<!-- H5 Start -->
<el-card shadow="never" class="m-t-15">
<template #header>
<span>H5</span>
</template>
<el-form-item label>
<div class="muted xs">
用户访问H5商城时使用注册设置里面的配置方法完成用户注册和登录
</div>
</el-form-item>
</el-card>
<!-- H5 End -->
<!-- APP设置 Start -->
<el-card shadow="never" class="m-t-15">
<template #header>
<span>APP设置</span>
</template>
<el-form-item label="微信授权登录">
<div class="flex col-center">
<el-switch v-model="formData.app_wechat_auth" :active-value="1" :inactive-value="0" />
<span class="m-l-16">{{ formData.app_wechat_auth ? '开启' : '关闭' }}</span>
</div>
<div class="muted xs">
APP是否支持微信授权登录方式关闭时使用注册设置里面的配置方法完成用户注册和登录
</div>
</el-form-item>
</el-card>
<!-- APP设置 End -->
</el-form>
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="onSubmit">保存</el-button>
</footer-btns>
<!-- Footer End -->
</div>
</template>
<script lang="ts" setup>
import {
reactive,
ref
} from 'vue'
import {
apiLoginConfigGet,
apiLoginConfigSet
} from '@/api/setting'
import FooterBtns from '@/components/footer-btns/index.vue'
/** Data Start **/
const formData = ref < any > ({
register_way: [], // 注册方式1-手机号码注册(注册设置必传)
login_way: [], //登录方式1-账号密码登录2-手机短信验证码登录
is_mobile_register_code: 0, // 手机号码注册需验证码0-关闭1-开启
coerce_mobile: 0, // 手机号码注册需验证码0-关闭1-开启
h5_wechat_auth: 0, // 微信公众号-微信授权登录0-关闭1-开启
h5_auto_wechat_auth: 1, // 微信公众号-自动微信授权登录:0-关闭1-开启;
mnp_wechat_auth: 0, // 小程序-微信授权登录 :0-关闭1-开启;
mnp_auto_wechat_auth: 1, // 小程序-自动微信授权登录:0-关闭1-开启;
app_wechat_auth: 0, // app-微信授权登录:0-关闭1-开启;
scene: '', // 场景user-用户设置register-注册设置withdraw-提现设置
toutiao_auth: 0,
toutiao_auto_auth: 0,
})
/** Data End **/
/** Methods Start **/
// 获取登录注册设置
const getLoginConfig = async (): Promise < void > => {
formData.value = await apiLoginConfigGet()
if(!formData.value.register_way) {
formData.value.register_way = []
}
if(!formData.value.login_way) {
formData.value.login_way = []
}
}
// 编辑登录注册设置
const handleLoginConfigEdit = async (): Promise < void > => {
await apiLoginConfigSet({
...formData.value
})
getLoginConfig()
}
// 提交数据
const onSubmit = () => {
handleLoginConfigEdit()
}
/** Methods End **/
/** LifeCycle Start **/
getLoginConfig()
/** LifeCycle End **/
</script>
<style lang="scss" scoped>
.login-register {
.item-a {
color: #1684FC;
text-decoration: underline;
}
}
</style>

View File

@@ -0,0 +1,138 @@
<!-- 备案信息 -->
<template>
<div class="website-filing">
<el-card shadow="never" class="">
<el-form
ref="form"
:rules="rules"
class="ls-form"
:model="formData"
label-width="150px"
size="small"
>
<!-- 版权信息输入框 -->
<el-form-item label="版权信息" prop="info">
<el-input v-model="formData.info" placeholder="请输入版权信息"></el-input>
<div class="muted xs m-r-16">例如填写Copyright © 2019-2020 公司名称</div>
</el-form-item>
<!-- ICP备案号输入框 -->
<el-form-item label="ICP备案号" prop="icp_number">
<el-input
v-model="formData.icp_number"
placeholder="请输入ICP备案号"
></el-input>
</el-form-item>
<!-- ICP备案号链接输入框 -->
<el-form-item label="ICP备案号链接" prop="icp_link">
<el-input
v-model="formData.icp_link"
placeholder="请输入ICP备案号链接"
></el-input>
<div class="muted xs m-r-16">
例如填写域名信息备案系统链接http://beian.miit.gov.cn
</div>
</el-form-item>
<!--公安备案号输入框 -->
<el-form-item label="公安备案号" prop="public_number">
<el-input
v-model="formData.public_number"
placeholder="请输入公安备案号"
></el-input>
</el-form-item>
<!-- 公安备案号链接输入框 -->
<el-form-item label="公安备案号链接" prop="public_link">
<el-input
v-model="formData.public_link"
placeholder="请输入公安备案号链接"
></el-input>
<div class="muted xs m-r-16">
例如填写公安信息备案系统链接http://www.beian.gov.cn
</div>
</el-form-item>
</el-form>
</el-card>
<footer-btns>
<el-button type="primary" size="small" @click="setCopyright">保存</el-button>
</footer-btns>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted } from 'vue'
import { apiGetCopyright, apiSetCopyright } from '@/api/setting'
import FooterBtns from '@/components/footer-btns/index.vue'
export default defineComponent({
components: {
FooterBtns
},
setup() {
// 表单数据
const formData = reactive({
info: '', // 版权信息
icp_number: '', // icp备案号
icp_link: '', // icp备案链接
public_number: '', // 公安备案号
public_link: '' // 公安备案链接
})
// 表单验证
const rules = {
// info: [
// {
// required: true,
// message: '请输入版权信息',
// trigger: ['blur'],
// },
// ],
}
// 获取备案信息
const getCopyright = () => {
apiGetCopyright().then((res: any) => {
console.log('res', res)
formData.info = res.info
formData.icp_number = res.icp_number
formData.icp_link = res.icp_link
formData.public_number = res.public_number
formData.public_link = res.public_link
})
}
// 设置备案信息
const setCopyright = () => {
apiSetCopyright({
info: formData.info,
icp_number: formData.icp_number,
icp_link: formData.icp_link,
public_number: formData.public_number,
public_link: formData.public_link
})
.then((res: any) => {
console.log('res', res)
getCopyright()
})
.catch((err: any) => {
console.log('err', err)
})
}
onMounted(() => {
getCopyright()
})
return {
formData,
rules,
getCopyright,
setCopyright
}
}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,155 @@
<!-- 网站信息 -->
<template>
<div class="website-information">
<el-card shadow="never" class="m-t-15">
<el-form
ref="formRef"
:rules="rules"
class="ls-form"
:model="formData"
label-width="150px"
size="small"
>
<el-form-item label="网站名称" prop="name">
<el-input
v-model="formData.name"
placeholder="请输入网站名称"
maxlength="12"
show-word-limit
></el-input>
</el-form-item>
<el-form-item label="网站图标" prop="web_favicon" required>
<material-select v-model="formData.web_favicon" :limit="1" />
<div class="flex">
<div class="muted xs m-r-16">
建议尺寸100*100像素支持jpgjpegpng格式
</div>
<el-popover placement="right" width="200" trigger="hover">
<el-image
src="https://img2.baidu.com/it/u=3357699356,1912406716&fm=26&fmt=auto&gp=0.jpg"
/>
<el-button slot="reference" type="text">查看示例</el-button>
</el-popover>
</div>
</el-form-item>
<el-form-item label="网站LOGO" prop="web_logo" required>
<material-select v-model="formData.web_logo" :limit="1" />
<div class="flex">
<div class="muted xs m-r-16">
建议尺寸100*100像素支持jpgjpegpng格式
</div>
<el-popover placement="right" width="200" trigger="hover">
<el-image
src="https://img2.baidu.com/it/u=3357699356,1912406716&fm=26&fmt=auto&gp=0.jpg"
/>
<el-button slot="reference" type="text">查看示例</el-button>
</el-popover>
</div>
</el-form-item>
<el-form-item label="登录页广告图" prop="login_image" required>
<material-select v-model="formData.login_image" :limit="1" />
<div class="flex">
<div class="muted xs m-r-16">
建议尺寸100*100像素支持jpgjpegpng格式
</div>
<el-popover placement="right" width="200" trigger="hover">
<el-image
src="https://img2.baidu.com/it/u=3357699356,1912406716&fm=26&fmt=auto&gp=0.jpg"
/>
<el-button slot="reference" type="text">查看示例</el-button>
</el-popover>
</div>
</el-form-item>
</el-form>
</el-card>
<footer-btns>
<el-button type="primary" size="small" @click="setWebsite">保存</el-button>
</footer-btns>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, Ref, ref } from 'vue'
import { ElInput, ElForm } from 'element-plus'
import MaterialSelect from '@/components/material-select/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import { apiGetWebsite, apiSetWebsite } from '@/api/setting'
export default defineComponent({
components: {
MaterialSelect,
FooterBtns
},
setup() {
const formRef: Ref<typeof ElForm | null> = ref(null)
// 表单数据
const formData = reactive({
name: '', // 网站名称
web_favicon: '', // 网站图标
web_logo: '', // 网站logo
login_image: '' // 登录页广告图
})
// 表单验证
const rules = {
name: [
{
required: true,
message: '请输入网站名称',
trigger: ['blur']
}
]
}
// 获取备案信息
const getWebsite = () => {
apiGetWebsite().then((res: any) => {
console.log('res', res)
formData.name = res.name
formData.web_favicon = res.web_favicon
formData.web_logo = res.web_logo
formData.login_image = res.login_image
})
}
// 设置备案信息
const setWebsite = () => {
formRef.value?.validate((valid: boolean) => {
if (!valid) {
return
}
console.log('fasdfasd')
apiSetWebsite({
name: formData.name,
web_favicon: formData.web_favicon,
web_logo: formData.web_logo,
login_image: formData.login_image
})
.then((res: any) => {
console.log('res', res)
getWebsite()
})
.catch((err: any) => {
console.log('err', err)
})
})
}
onMounted(() => {
getWebsite()
})
return {
formData,
rules,
getWebsite,
setWebsite,
formRef
}
}
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,94 @@
<template>
<!-- 服务协议 Start -->
<el-card shadow="never">
<template #header>
<span class="md f-w-600">服务协议</span>
</template>
<!-- Header Form Start -->
<el-form :model="formData" label-width="80px" size="mini" inline>
<el-form-item label="协议名称">
<el-input class="ls-input" v-model="formData.service_title" />
</el-form-item>
</el-form>
<!-- Header Form End -->
<!-- Main Editor Start -->
<editor
v-model="formData.service_content"
width="1000"
height="664"
class="m-l-14"
></editor>
<!-- Main Editor End -->
</el-card>
<!-- 服务协议 End -->
<!-- 隐私协议 Start -->
<el-card shadow="never" class="m-t-15">
<template #header>
<span class="md f-w-600">隐私协议</span>
</template>
<!-- Header Form Start -->
<el-form :model="formData" label-width="80px" size="mini" inline>
<el-form-item label="协议名称">
<el-input class="ls-input" v-model="formData.privacy_title" />
</el-form-item>
</el-form>
<!-- Header Form End -->
<!-- Main Editor Start -->
<editor
v-model="formData.privacy_content"
width="1000"
height="664"
class="m-l-14"
></editor>
<!-- Main Editor End -->
</el-card>
<!-- 政策协议 End -->
<!-- Footer Start -->
<footer-btns>
<el-button type="primary" size="small" @click="handleProtocolEdit">保存</el-button>
</footer-btns>
<!-- Footer End -->
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { apiGetProtocol, apiSetProtocol } from '@/api/setting'
import Editor from '@/components/editor/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
/** Interface Star **/
interface formDataObj {
service_title: string
service_content: string
privacy_title: string
privacy_content: string
}
/** Interface End **/
/** Data Start **/
let formData = ref<formDataObj>({
service_title: '',
service_content: '',
privacy_title: '',
privacy_content: '',
})
/** Data End **/
/** Methods Start **/
const protocolGet = async (): Promise<void> => {
;(formData.value as object) = await apiGetProtocol()
}
const handleProtocolEdit = async (): Promise<void> => {
await apiSetProtocol({ ...formData.value })
protocolGet()
}
/** Methods End **/
/** Life Cycle Start **/
protocolGet()
/** Life Cycle End **/
</script>

View File

@@ -0,0 +1,263 @@
<template>
<div class="workbench">
<div class="flex workbench-header">
<el-card class="flex-1" shadow="never">
<template #header>
<span class="card-title">账号信息</span>
</template>
<div>
<div class="m-b-20">版本号{{ workbenchData.version.version }}</div>
<div>官网地址{{ workbenchData.version.website }}</div>
</div>
</el-card>
<el-card class="flex-3 m-l-15" shadow="never">
<template #header>
<div>
<span class="card-title">今日数据</span>
<span class="muted xs m-l-15"
>更新时间{{ workbenchData.today.time }}</span
>
</div>
</template>
<div class="flex">
<div class="flex-1">
<div class="lighter m-b-10">访问量</div>
<div class="f-s-32 m-b-10">
{{ workbenchData.today.today_visitor }}
</div>
<div class="lighter">总访问量{{ workbenchData.today.total_visitor }}</div>
</div>
<div class="flex-1">
<div class="lighter m-b-10">销售额</div>
<div class="f-s-32 m-b-10">
{{ workbenchData.today.today_sales }}
</div>
<div class="lighter">总销售额{{ workbenchData.today.total_sales }}</div>
</div>
<div class="flex-1">
<div class="lighter m-b-10">新增用户</div>
<div class="f-s-32 m-b-10">
{{ workbenchData.today.today_new_user }}
</div>
<div class="lighter">
总访用户{{ workbenchData.today.total_new_user }}
</div>
</div>
</div>
</el-card>
</div>
<div class="m-t-15 function">
<el-card class="flex-1" shadow="never">
<template #header>
<span class="card-title">常用功能</span>
</template>
<div class="nav-lists">
<div
v-for="item in workbenchData.menu"
:key="item"
class="nav-item flex flex-col flex-center m-t-10"
>
<router-link :to="item.url">
<el-image style="width: 48px; height: 48px" :src="item.image">
</el-image>
<div class="m-t-8 normal">{{ item.name }}</div>
</router-link>
</div>
</div>
</el-card>
</div>
<div class="flex m-t-15 ranking">
<el-card class="flex-1" shadow="never">
<template #header>
<span class="card-title">访问量趋势图</span>
</template>
<div class="ranking-centent">
<v-chart class="chart" :option="workbenchData.visitorOption" />
</div>
</el-card>
<el-card class="flex-1 m-l-15" shadow="never">
<template #header>
<div>
<span class="card-title">文章阅读量排名</span>
</div>
</template>
<div class="ranking-centent">
<el-table :data="workbenchData.article" size="medium">
<el-table-column label="排名" min-width="70" type="index">
<template #default="scope">
<div
v-if="scope.$index == 0"
class="icon"
style="background: #f86056; color: #fff"
>
{{ scope.$index + 1 }}
</div>
<div
v-if="scope.$index == 1"
class="icon"
style="background: #fc8d2e; color: #fff"
>
{{ scope.$index + 1 }}
</div>
<div
v-if="scope.$index == 2"
class="icon"
style="background: #fcbc2e; color: #fff"
>
{{ scope.$index + 1 }}
</div>
<div v-if="scope.$index > 2" class="icon">
{{ scope.$index + 1 }}
</div>
</template>
</el-table-column>
<el-table-column prop="name" label="文章名称"> </el-table-column>
<el-table-column prop="read" label="阅读量"> </el-table-column>
</el-table>
</div>
</el-card>
</div>
<!-- <material-select /> -->
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted } from 'vue'
import { apiWorkbench } from '@/api/workbench'
export default defineComponent({
setup() {
// 表单数据
const workbenchData = reactive({
version: {
version: '', // 版本号
website: '' // 官网
},
today: {}, // 今日数据
menu: [], // 常用功能
visitor: [], // 访问量
article: [], // 文章阅读量
visitorOption: {
xAxis: {
type: 'category',
data: [0]
},
yAxis: {
type: 'value'
},
legend: {
data: ['访问量']
},
itemStyle: {
// 点的颜色。
color: 'red'
},
tooltip: {
trigger: 'axis'
},
series: [
{
name: '访问量',
data: [0],
type: 'line'
//smooth: true,
}
]
}
})
// 获取工作台主页数据
const getWorkbench = () => {
apiWorkbench()
.then((res: any) => {
console.log('res', res)
workbenchData.version = res.version
workbenchData.today = res.today
workbenchData.menu = res.menu
workbenchData.visitor = res.visitor
workbenchData.article = res.article
// 清空echarts 数据
workbenchData.visitorOption.xAxis.data = []
workbenchData.visitorOption.series[0].data = []
// 写入从后台拿来的数据
res.visitor.date.reverse().forEach((item: any) => {
workbenchData.visitorOption.xAxis.data.push(item)
})
res.visitor.list[0].data.forEach((item: any) => {
workbenchData.visitorOption.series[0].data.push(item)
})
})
.catch((err: any) => {
console.log('err', err)
})
}
onMounted(() => {
getWorkbench()
})
return {
workbenchData,
getWorkbench
}
}
})
</script>
<style lang="scss" scoped>
.workbench {
.card-title {
position: relative;
font-weight: 500;
padding-left: 10px;
font-size: 14px;
&::before {
position: absolute;
content: '';
display: inline-block;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 3px;
height: 80%;
background: $color-primary;
}
}
.function {
.nav-lists {
display: flex;
flex-wrap: wrap;
.nav-item {
min-width: 120px;
}
}
}
.ranking {
&-centent {
height: 530px;
}
}
.icon {
width: 25px;
height: 25px;
color: #333;
border-radius: 2px;
background: #f5f5f5;
font-family: 'PingFang SC';
font-weight: normal;
font-size: 12px;
line-height: 25px;
text-align: center;
}
}
</style>