Add configurable contact page management
This commit is contained in:
9
ruoyi-ui/src/api/app/contact.js
Normal file
9
ruoyi-ui/src/api/app/contact.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getContactConfig() {
|
||||
return request({ url: '/app/contact', method: 'get' })
|
||||
}
|
||||
|
||||
export function updateContactConfig(data) {
|
||||
return request({ url: '/app/contact', method: 'put', data })
|
||||
}
|
||||
270
ruoyi-ui/src/views/app/contact/index.vue
Normal file
270
ruoyi-ui/src/views/app/contact/index.vue
Normal file
@@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<div v-loading="loading" class="app-container contact-config">
|
||||
<el-alert
|
||||
title="这里保存后,小程序会动态展示启用的联系方式;页面导航标题保持现状。"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="page-alert"
|
||||
/>
|
||||
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="96px">
|
||||
<el-card shadow="never" class="config-card">
|
||||
<div slot="header" class="card-header">
|
||||
<div>
|
||||
<div class="card-title">页面资料</div>
|
||||
<div class="card-desc">配置头像、昵称和简介,背景图不上传时使用默认渐变背景。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="28">
|
||||
<el-col :xs="24" :sm="12">
|
||||
<el-form-item label="头像" prop="avatarUrl">
|
||||
<image-upload v-model="form.avatarUrl" :limit="1" :file-size="5" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12">
|
||||
<el-form-item label="顶部背景图" prop="backgroundUrl">
|
||||
<image-upload v-model="form.backgroundUrl" :limit="1" :file-size="8" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="28">
|
||||
<el-col :xs="24" :sm="12">
|
||||
<el-form-item label="昵称" prop="nickname">
|
||||
<el-input v-model.trim="form.nickname" maxlength="100" show-word-limit placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12">
|
||||
<el-form-item label="个人简介" prop="bio">
|
||||
<el-input v-model.trim="form.bio" maxlength="500" show-word-limit placeholder="请输入个人简介" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="config-card">
|
||||
<div slot="header" class="card-header">
|
||||
<div>
|
||||
<div class="card-title">联系方式</div>
|
||||
<div class="card-desc">可新增、删除、启停和排序;未上传图标时小程序会使用对应类型的文字图标。</div>
|
||||
</div>
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="small" @click="addContact">新增联系方式</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="form.contacts" border row-key="rowKey" empty-text="暂无联系方式,请点击右上角新增">
|
||||
<el-table-column label="排序" width="90" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number v-model="scope.row.sort" :min="0" :max="9999" :controls="false" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="130">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.type" size="small" @change="applyTypePreset(scope.row)">
|
||||
<el-option v-for="item in contactTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="显示名称" min-width="145">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model.trim="scope.row.label" maxlength="50" size="small" placeholder="如:微信" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系内容" min-width="220">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model.trim="scope.row.value" maxlength="500" size="small" placeholder="账号、邮箱或网址" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="点击操作" width="130">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.actionType" size="small">
|
||||
<el-option label="复制内容" value="copy" />
|
||||
<el-option label="查看二维码" value="qrcode" :disabled="!scope.row.qrCodeUrl" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="自定义图标" width="112" align="center">
|
||||
<template slot-scope="scope">
|
||||
<image-upload v-model="scope.row.iconUrl" class="compact-uploader" :limit="1" :file-size="2" :is-show-tip="false" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="二维码" width="112" align="center">
|
||||
<template slot-scope="scope">
|
||||
<image-upload v-model="scope.row.qrCodeUrl" class="compact-uploader" :limit="1" :file-size="5" :is-show-tip="false" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" class="danger-button" @click="removeContact(scope.$index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<div class="save-bar">
|
||||
<el-button type="primary" icon="el-icon-check" :loading="saving" @click="submitForm">保存配置</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getContactConfig, updateContactConfig } from '@/api/app/contact'
|
||||
|
||||
export default {
|
||||
name: 'ContactConfig',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
rowSequence: 0,
|
||||
contactTypes: [
|
||||
{ label: 'QQ', value: 'qq' },
|
||||
{ label: '微信', value: 'wechat' },
|
||||
{ label: '邮箱', value: 'email' },
|
||||
{ label: '博客/网站', value: 'blog' },
|
||||
{ label: '自定义', value: 'custom' }
|
||||
],
|
||||
form: this.emptyForm(),
|
||||
rules: {
|
||||
nickname: [{ required: true, message: '请输入昵称', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadConfig()
|
||||
},
|
||||
methods: {
|
||||
emptyForm() {
|
||||
return { id: 1, avatarUrl: '', nickname: '', bio: '', backgroundUrl: '', contacts: [] }
|
||||
},
|
||||
loadConfig() {
|
||||
this.loading = true
|
||||
getContactConfig().then(response => {
|
||||
const data = response.data || this.emptyForm()
|
||||
data.contacts = (data.contacts || []).map(item => this.withRowKey(item))
|
||||
this.form = data
|
||||
}).finally(() => { this.loading = false })
|
||||
},
|
||||
withRowKey(item) {
|
||||
this.rowSequence += 1
|
||||
return Object.assign({
|
||||
id: undefined,
|
||||
type: 'custom',
|
||||
label: '',
|
||||
value: '',
|
||||
iconUrl: '',
|
||||
qrCodeUrl: '',
|
||||
actionType: 'copy',
|
||||
status: '0',
|
||||
sort: this.rowSequence * 10
|
||||
}, item, { rowKey: `contact-${this.rowSequence}` })
|
||||
},
|
||||
addContact() {
|
||||
if (this.form.contacts.length >= 30) {
|
||||
this.$modal.msgWarning('最多可配置30条联系方式')
|
||||
return
|
||||
}
|
||||
this.form.contacts.push(this.withRowKey({ sort: (this.form.contacts.length + 1) * 10 }))
|
||||
},
|
||||
removeContact(index) {
|
||||
this.form.contacts.splice(index, 1)
|
||||
},
|
||||
applyTypePreset(row) {
|
||||
const labels = { qq: 'QQ', wechat: '微信', email: '邮箱', blog: '博客' }
|
||||
if (labels[row.type] && !row.label) row.label = labels[row.type]
|
||||
if (row.type === 'email' || row.type === 'blog') row.actionType = 'copy'
|
||||
},
|
||||
validateContacts() {
|
||||
for (let i = 0; i < this.form.contacts.length; i += 1) {
|
||||
const item = this.form.contacts[i]
|
||||
if (!item.label) return `第${i + 1}条联系方式缺少显示名称`
|
||||
if (!item.value) return `第${i + 1}条联系方式缺少联系内容`
|
||||
if (item.actionType === 'qrcode' && !item.qrCodeUrl) return `第${i + 1}条联系方式选择了二维码操作,但尚未上传二维码`
|
||||
}
|
||||
return ''
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) return
|
||||
const message = this.validateContacts()
|
||||
if (message) {
|
||||
this.$modal.msgWarning(message)
|
||||
return
|
||||
}
|
||||
this.saving = true
|
||||
const payload = JSON.parse(JSON.stringify(this.form))
|
||||
payload.contacts.forEach(item => { delete item.rowKey })
|
||||
updateContactConfig(payload).then(() => {
|
||||
this.$modal.msgSuccess('保存成功')
|
||||
this.loadConfig()
|
||||
}).finally(() => { this.saving = false })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contact-config {
|
||||
padding-bottom: 84px;
|
||||
}
|
||||
.page-alert {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.config-card {
|
||||
margin-bottom: 18px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card-title {
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.card-desc {
|
||||
margin-top: 6px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
.danger-button {
|
||||
color: #f56c6c;
|
||||
}
|
||||
.save-bar {
|
||||
position: sticky;
|
||||
z-index: 20;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: 0 -20px -84px;
|
||||
padding: 14px 24px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 -4px 14px rgba(31, 45, 61, 0.06);
|
||||
}
|
||||
::v-deep .compact-uploader .el-upload--picture-card,
|
||||
::v-deep .compact-uploader .el-upload-list--picture-card .el-upload-list__item {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
line-height: 58px;
|
||||
margin: 0;
|
||||
}
|
||||
::v-deep .compact-uploader .el-upload-list--picture-card .el-upload-list__item-actions {
|
||||
font-size: 14px;
|
||||
}
|
||||
::v-deep .el-input-number {
|
||||
width: 62px;
|
||||
}
|
||||
@media (max-width: 992px) {
|
||||
.card-header { align-items: flex-start; gap: 16px; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user