branch_yhy1
yue 2 months ago
parent f39e625538
commit 1a03ca8339

@ -1,132 +1,168 @@
<template>
<div class="mod-role">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.roleName" placeholder="角色名称" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()"></el-button>
<el-button v-if="isAuth('sys:role:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<el-button v-if="isAuth('sys:role:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<el-table-column prop="roleId" header-align="center" align="center" width="80" label="ID">
</el-table-column>
<el-table-column prop="roleName" header-align="center" align="center" label="角色名称">
</el-table-column>
<el-table-column prop="remark" header-align="center" align="center" label="备注">
</el-table-column>
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<el-button v-if="isAuth('sys:role:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.roleId)"></el-button>
<el-button v-if="isAuth('sys:role:delete')" type="text" size="small" @click="deleteHandle(scope.row.roleId)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<template>
<div class="mod-role">
<!-- 搜索和管理角色的表单 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<!-- 角色名称输入框 -->
<el-input v-model="dataForm.roleName" placeholder="角色名称" clearable></el-input>
</el-form-item>
<el-form-item>
<!-- 查询按钮 -->
<el-button @click="getDataList()"></el-button>
<!-- 新增按钮只有在有权限时才显示 -->
<el-button v-if="isAuth('sys:role:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮只有在有权限时才显示且未选择则禁用 -->
<el-button v-if="isAuth('sys:role:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<!-- 显示角色的表格 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择列用于多选 -->
<el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column>
<!-- ID列 -->
<el-table-column prop="roleId" header-align="center" align="center" width="80" label="ID"></el-table-column>
<!-- 角色名称列 -->
<el-table-column prop="roleName" header-align="center" align="center" label="角色名称"></el-table-column>
<!-- 备注列 -->
<el-table-column prop="remark" header-align="center" align="center" label="备注"></el-table-column>
<!-- 操作列 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<!-- 修改按钮只有在有权限时才显示 -->
<el-button v-if="isAuth('sys:role:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.roleId)"></el-button>
<!-- 删除按钮只有在有权限时才显示 -->
<el-button v-if="isAuth('sys:role:delete')" type="text" size="small" @click="deleteHandle(scope.row.roleId)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './role-add-or-update'
export default {
data() {
return {
dataForm: {
roleName: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated() {
this.getDataList()
},
methods: {
//
getDataList() {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/role/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'roleName': this.dataForm.roleName
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount = data.page.totalCount
} else {
this.dataList = []
this.totalCount = 0
}
this.dataListLoading = false
})
},
//
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
selectionChangeHandle(val) {
this.dataListSelections = val
},
// /
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
//
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.roleId)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/role/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList()
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => { })
}
}
}
</script>
<script>
import AddOrUpdate from './role-add-or-update' // /
export default {
data() {
return {
//
dataForm: {
roleName: ''
},
//
dataList: [],
//
pageIndex: 1,
//
pageSize: 10,
//
totalCount: 0,
//
dataListLoading: false,
//
dataListSelections: [],
//
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate // /
},
activated() {
//
this.getDataList()
},
methods: {
//
getDataList() {
this.dataListLoading = true //
this.$http({
url: this.$http.adornUrl('/sys/role/list'), // API
method: 'get', // HTTP
params: this.$http.adornParams({
'page': this.pageIndex, //
'limit': this.pageSize, //
'roleName': this.dataForm.roleName //
})
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.dataList = data.page.list //
this.totalCount = data.page.totalCount //
} else {
//
this.dataList = []
this.totalCount = 0
}
this.dataListLoading = false //
})
},
//
sizeChangeHandle(val) {
this.pageSize = val //
this.pageIndex = 1 //
this.getDataList() //
},
//
currentChangeHandle(val) {
this.pageIndex = val //
this.getDataList() //
},
//
selectionChangeHandle(val) {
this.dataListSelections = val //
},
// /
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true //
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id) // ID
})
},
//
deleteHandle(id) {
// ID
var ids = id ? [id] : this.dataListSelections.map(item => item.roleId)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//
this.$http({
url: this.$http.adornUrl('/sys/role/delete'), // API
method: 'post', // HTTP
data: this.$http.adornData(ids, false) // ID
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList() //
})
} else {
//
this.$message.error(data.msg) //
}
})
}).catch(() => { }) //
}
}
}
</script>

@ -1,177 +1,202 @@
<template>
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="用户名" prop="userName">
<el-input v-model="dataForm.userName" placeholder="登录帐号"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password" :class="{ 'is-required': !dataForm.id }">
<el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="comfirmPassword" :class="{ 'is-required': !dataForm.id }">
<el-input v-model="dataForm.comfirmPassword" type="password" placeholder="确认密码"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="dataForm.email" placeholder="邮箱"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="dataForm.mobile" placeholder="手机号"></el-input>
</el-form-item>
<el-form-item label="角色" size="mini" prop="roleIdList">
<el-checkbox-group v-model="dataForm.roleIdList">
<el-checkbox v-for="role in roleList" :key="role.roleId" :label="role.roleId">{{ role.roleName }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="状态" size="mini" prop="status">
<el-radio-group v-model="dataForm.status">
<el-radio :label="0">禁用</el-radio>
<el-radio :label="1">正常</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<template>
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<!-- 表单使用dataForm作为模型进行用户信息的新增或修改 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<!-- 用户名输入框 -->
<el-form-item label="用户名" prop="userName">
<el-input v-model="dataForm.userName" placeholder="登录帐号"></el-input>
</el-form-item>
<!-- 密码输入框若为新增则必填 -->
<el-form-item label="密码" prop="password" :class="{ 'is-required': !dataForm.id }">
<el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input>
</el-form-item>
<!-- 确认密码输入框若为新增则必填 -->
<el-form-item label="确认密码" prop="comfirmPassword" :class="{ 'is-required': !dataForm.id }">
<el-input v-model="dataForm.comfirmPassword" type="password" placeholder="确认密码"></el-input>
</el-form-item>
<!-- 邮箱输入框 -->
<el-form-item label="邮箱" prop="email">
<el-input v-model="dataForm.email" placeholder="邮箱"></el-input>
</el-form-item>
<!-- 手机号输入框 -->
<el-form-item label="手机号" prop="mobile">
<el-input v-model="dataForm.mobile" placeholder="手机号"></el-input>
</el-form-item>
<!-- 角色选择 -->
<el-form-item label="角色" size="mini" prop="roleIdList">
<el-checkbox-group v-model="dataForm.roleIdList">
<el-checkbox v-for="role in roleList" :key="role.roleId" :label="role.roleId">{{ role.roleName }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 状态选择 -->
<el-form-item label="状态" size="mini" prop="status">
<el-radio-group v-model="dataForm.status">
<el-radio :label="0">禁用</el-radio>
<el-radio :label="1">正常</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- 对话框底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<script>
import { isEmail, isMobile } from '@/utils/validate'
export default {
data() {
var validatePassword = (rule, value, callback) => {
if (!this.dataForm.id && !/\S/.test(value)) {
callback(new Error('密码不能为空'))
} else {
callback()
}
}
var validateComfirmPassword = (rule, value, callback) => {
if (!this.dataForm.id && !/\S/.test(value)) {
callback(new Error('确认密码不能为空'))
} else if (this.dataForm.password !== value) {
callback(new Error('确认密码与密码输入不一致'))
} else {
callback()
}
}
var validateEmail = (rule, value, callback) => {
if (!isEmail(value)) {
callback(new Error('邮箱格式错误'))
} else {
callback()
}
}
var validateMobile = (rule, value, callback) => {
if (!isMobile(value)) {
callback(new Error('手机号格式错误'))
} else {
callback()
}
}
return {
visible: false,
roleList: [],
dataForm: {
id: 0,
userName: '',
password: '',
comfirmPassword: '',
salt: '',
email: '',
mobile: '',
roleIdList: [],
status: 1
},
dataRule: {
userName: [
{ required: true, message: '用户名不能为空', trigger: 'blur' }
],
password: [
{ validator: validatePassword, trigger: 'blur' }
],
comfirmPassword: [
{ validator: validateComfirmPassword, trigger: 'blur' }
],
email: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
{ validator: validateEmail, trigger: 'blur' }
],
mobile: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{ validator: validateMobile, trigger: 'blur' }
]
}
}
},
methods: {
init(id) {
this.dataForm.id = id || 0
this.$http({
url: this.$http.adornUrl('/sys/role/select'),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
this.roleList = data && data.code === 200 ? data.list : []
}).then(() => {
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
}).then(() => {
if (this.dataForm.id) {
this.$http({
url: this.$http.adornUrl(`/sys/user/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataForm.userName = data.user.username
this.dataForm.salt = data.user.salt
this.dataForm.email = data.user.email
this.dataForm.mobile = data.user.mobile
this.dataForm.roleIdList = data.user.roleIdList
this.dataForm.status = data.user.status
}
})
}
})
},
//
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/sys/user/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'userId': this.dataForm.id || undefined,
'username': this.dataForm.userName,
'password': this.dataForm.password,
'salt': this.dataForm.salt,
'email': this.dataForm.email,
'mobile': this.dataForm.mobile,
'status': this.dataForm.status,
'roleIdList': this.dataForm.roleIdList
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<script>
import { isEmail, isMobile } from '@/utils/validate' //
export default {
data() {
//
var validatePassword = (rule, value, callback) => {
if (!this.dataForm.id && !/\S/.test(value)) { //
callback(new Error('密码不能为空'))
} else {
callback() //
}
}
var validateComfirmPassword = (rule, value, callback) => {
if (!this.dataForm.id && !/\S/.test(value)) { //
callback(new Error('确认密码不能为空'))
} else if (this.dataForm.password !== value) { //
callback(new Error('确认密码与密码输入不一致'))
} else {
callback() //
}
}
//
var validateEmail = (rule, value, callback) => {
if (!isEmail(value)) {
callback(new Error('邮箱格式错误'))
} else {
callback() //
}
}
//
var validateMobile = (rule, value, callback) => {
if (!isMobile(value)) {
callback(new Error('手机号格式错误'))
} else {
callback() //
}
}
return {
visible: false, //
roleList: [], //
//
dataForm: {
id: 0, // ID
userName: '', //
password: '', //
comfirmPassword: '', //
salt: '', //
email: '', //
mobile: '', //
roleIdList: [], // ID
status: 1 // 1: , 0:
},
//
dataRule: {
userName: [
{ required: true, message: '用户名不能为空', trigger: 'blur' }
],
password: [
{ validator: validatePassword, trigger: 'blur' }
],
comfirmPassword: [
{ validator: validateComfirmPassword, trigger: 'blur' }
],
email: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
{ validator: validateEmail, trigger: 'blur' }
],
mobile: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{ validator: validateMobile, trigger: 'blur' }
]
}
}
},
methods: {
//
init(id) {
this.dataForm.id = id || 0 // ID
//
this.$http({
url: this.$http.adornUrl('/sys/role/select'),
method: 'get',
params: this.$http.adornParams() //
}).then(({ data }) => {
this.roleList = data && data.code === 200 ? data.list : [] //
}).then(() => {
this.visible = true //
this.$nextTick(() => {
this.$refs['dataForm'].resetFields() //
})
}).then(() => {
//
if (this.dataForm.id) {
this.$http({
url: this.$http.adornUrl(`/sys/user/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.dataForm.userName = data.user.username //
this.dataForm.salt = data.user.salt // 使
this.dataForm.email = data.user.email //
this.dataForm.mobile = data.user.mobile //
this.dataForm.roleIdList = data.user.roleIdList // ID
this.dataForm.status = data.user.status //
}
})
}
})
},
//
dataFormSubmit() {
//
this.$refs['dataForm'].validate((valid) => {
if (valid) { //
this.$http({
// ID
url: this.$http.adornUrl(`/sys/user/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
//
'userId': this.dataForm.id || undefined,
'username': this.dataForm.userName,
'password': this.dataForm.password,
'salt': this.dataForm.salt,
'email': this.dataForm.email,
'mobile': this.dataForm.mobile,
'status': this.dataForm.status,
'roleIdList': this.dataForm.roleIdList
})
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false //
this.$emit('refreshDataList') //
}
})
} else {
//
this.$message.error(data.msg) //
}
})
}
})
}
}
}
</script>

@ -1,140 +1,168 @@
<template>
<div class="mod-user">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.userName" placeholder="用户名" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()"></el-button>
<el-button v-if="isAuth('sys:user:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<el-button v-if="isAuth('sys:user:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<el-table-column prop="userId" header-align="center" align="center" width="80" label="ID">
</el-table-column>
<el-table-column prop="username" header-align="center" align="center" label="用户名">
</el-table-column>
<el-table-column prop="email" header-align="center" align="center" label="邮箱">
</el-table-column>
<el-table-column prop="mobile" header-align="center" align="center" label="手机号">
</el-table-column>
<el-table-column prop="status" header-align="center" align="center" label="状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 0" size="small" type="danger"></el-tag>
<el-tag v-else size="small">正常</el-tag>
</template>
</el-table-column>
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<el-button v-if="isAuth('sys:user:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.userId)"></el-button>
<el-button v-if="isAuth('sys:user:delete')" type="text" size="small" @click="deleteHandle(scope.row.userId)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<template>
<div class="mod-user">
<!-- 搜索表单 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<!-- 用户名输入框 -->
<el-input v-model="dataForm.userName" placeholder="用户名" clearable></el-input>
</el-form-item>
<el-form-item>
<!-- 查询按钮 -->
<el-button @click="getDataList()"></el-button>
<!-- 新增按钮只有在有权限时显示 -->
<el-button v-if="isAuth('sys:user:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮只有在有权限时显示并且在没有选择项时禁用 -->
<el-button v-if="isAuth('sys:user:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<!-- 用户数据表格 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择框列 -->
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<!-- ID列 -->
<el-table-column prop="userId" header-align="center" align="center" width="80" label="ID">
</el-table-column>
<!-- 用户名列 -->
<el-table-column prop="username" header-align="center" align="center" label="用户名">
</el-table-column>
<!-- 邮箱列 -->
<el-table-column prop="email" header-align="center" align="center" label="邮箱">
</el-table-column>
<!-- 手机号列 -->
<el-table-column prop="mobile" header-align="center" align="center" label="手机号">
</el-table-column>
<!-- 状态列 -->
<el-table-column prop="status" header-align="center" align="center" label="状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 0" size="small" type="danger"></el-tag>
<el-tag v-else size="small">正常</el-tag>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<!-- 修改按钮只有在有权限时显示 -->
<el-button v-if="isAuth('sys:user:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.userId)"></el-button>
<!-- 删除按钮只有在有权限时显示 -->
<el-button v-if="isAuth('sys:user:delete')" type="text" size="small" @click="deleteHandle(scope.row.userId)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗用于新增或修改用户 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './user-add-or-update'
export default {
data() {
return {
dataForm: {
userName: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalCount: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},
components: {
AddOrUpdate
},
activated() {
this.getDataList()
},
methods: {
//
getDataList() {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/user/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'username': this.dataForm.userName
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount = data.page.totalCount
} else {
this.dataList = []
this.totalCount = 0
}
this.dataListLoading = false
})
},
//
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
selectionChangeHandle(val) {
this.dataListSelections = val
},
// /
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
//
deleteHandle(id) {
var userIds = id ? [id] : this.dataListSelections.map(item => item.userId)
this.$confirm(`确定对[id=${userIds.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/user/delete'),
method: 'post',
data: this.$http.adornData(userIds, false)
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList()
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => { })
}
}
}
</script>
<script>
import AddOrUpdate from './user-add-or-update' // /
export default {
data() {
return {
dataForm: {
userName: '' //
},
dataList: [], //
pageIndex: 1, //
pageSize: 10, //
totalCount: 0, //
dataListLoading: false, //
dataListSelections: [], //
addOrUpdateVisible: false // /
}
},
components: {
AddOrUpdate // /
},
activated() {
this.getDataList() //
},
methods: {
//
getDataList() {
this.dataListLoading = true // true
this.$http({
url: this.$http.adornUrl('/sys/user/list'), // API
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex, //
'limit': this.pageSize, //
'username': this.dataForm.userName //
})
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.dataList = data.page.list
this.totalCount = data.page.totalCount
} else {
//
this.dataList = []
this.totalCount = 0
}
this.dataListLoading = false // false
})
},
//
sizeChangeHandle(val) {
this.pageSize = val //
this.pageIndex = 1 //
this.getDataList() //
},
//
currentChangeHandle(val) {
this.pageIndex = val //
this.getDataList() //
},
//
selectionChangeHandle(val) {
this.dataListSelections = val //
},
//
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true // /
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id) // ID
})
},
//
deleteHandle(id) {
// ID
var userIds = id ? [id] : this.dataListSelections.map(item => item.userId)
this.$confirm(`确定对[id=${userIds.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//
this.$http({
url: this.$http.adornUrl('/sys/user/delete'), // API
method: 'post',
data: this.$http.adornData(userIds, false) // ID
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList() //
})
} else {
//
this.$message.error(data.msg)
}
})
}).catch(() => {
//
})
}
}
}
</script>

@ -1,54 +1,63 @@
<template>
<el-dialog title="开发接入信息" :close-on-click-modal="false" :visible.sync="visible">
<div>
<div class="list-item"><span class="label">公众号:</span>{{account.name}}</div>
<div class="list-item"><span class="label">token:</span>{{account.token}}</div>
<div class="list-item"><span class="label">aesKey:</span>{{account.aesKey}}</div>
<div class="list-item">
<span class="label">接入链接:</span>
<span v-html="accessUrl"></span>
</div>
</div>
</el-dialog>
</template>
<template>
<!-- 开发接入信息对话框 -->
<el-dialog title="开发接入信息" :close-on-click-modal="false" :visible.sync="visible">
<div>
<!-- 公众号信息 -->
<div class="list-item"><span class="label">公众号:</span>{{account.name}}</div>
<!-- token 信息 -->
<div class="list-item"><span class="label">token:</span>{{account.token}}</div>
<!-- aesKey 信息 -->
<div class="list-item"><span class="label">aesKey:</span>{{account.aesKey}}</div>
<!-- 接入链接 -->
<div class="list-item">
<span class="label">接入链接:</span>
<span v-html="accessUrl"></span> <!-- 使 v-html -->
</div>
</div>
</el-dialog>
</template>
<script>
export default {
data() {
return {
visible: false,
account: {},
}
},
computed: {
accessUrl() {
let host = location.host;
if(/^(\d(.\d){3})|localhost/.test(host)){
host='<span class="text-red">正式域名</span>'
}
return location.protocol + '//' + host + '/wx/wx/msg/' + this.account.appid
}
},
methods: {
init(item) {
this.visible = true
if (item && item.appid) {
this.account = item
}
},
}
}
</script>
<style scoped>
.list-item{
line-height: 30px;
}
.label{
width: 100px;
display: inline-block;
margin-right: 10px;
font-weight: bold;
text-align: right;
}
</style>
<script>
export default {
data() {
return {
visible: false, //
account: {}, //
}
},
computed: {
//
accessUrl() {
let host = location.host; //
// IP localhost
if(/^(\d(.\d){3})|localhost/.test(host)){
host='<span class="text-red">正式域名</span>' // IP localhost
}
//
return location.protocol + '//' + host + '/wx/wx/msg/' + this.account.appid
}
},
methods: {
//
init(item) {
this.visible = true //
if (item && item.appid) {
this.account = item // appid account
}
},
}
}
</script>
<style scoped>
.list-item{
line-height: 30px; /* 每一项的行高 */
}
.label{
width: 100px; /* 标签的宽度 */
display: inline-block; /* 使标签在每行中内联显示 */
margin-right: 10px; /* 标签与内容之间的右边距 */
font-weight: bold; /* 标签加粗 */
text-align: right; /* 标签文本右对齐 */
}
</style>

@ -1,118 +1,145 @@
<template>
<el-dialog
title="新增/修改"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="100px">
<el-form-item label="公众号名称" prop="name">
<el-input v-model="dataForm.name" placeholder="公众号名称"></el-input>
</el-form-item>
<div class="padding text-gray">测试号可选择服务号不同类型账号是否认证可使用功能权限不同<a href="https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.html">参考文档</a></div>
<el-row>
<el-col :span="12">
<el-form-item label="公众号类型" prop="type">
<el-select v-model="dataForm.type" placeholder="公众号类型">
<el-option v-for="(name,key) in ACCOUNT_TYPES" :key="name" :label="name" :value="key"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否认证" prop="verified">
<el-switch v-model="dataForm.verified" placeholder="是否认证"></el-switch>
</el-form-item>
</el-col>
</el-row>
<template>
<!-- 新增/修改对话框 -->
<el-dialog
title="新增/修改"
:close-on-click-modal="false"
:visible.sync="visible">
<el-form-item label="appid" prop="appid">
<el-input v-model="dataForm.appid" placeholder="appid"></el-input>
</el-form-item>
<el-form-item label="appsecret" prop="secret">
<el-input v-model="dataForm.secret" placeholder="appsecret"></el-input>
</el-form-item>
<el-form-item label="token" prop="token">
<el-input v-model="dataForm.token" placeholder="token"></el-input>
</el-form-item>
<el-form-item label="aesKey" prop="aesKey">
<el-input v-model="dataForm.aesKey" placeholder="aesKey可为空"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
visible: false,
dataForm: {
appid: '',
name: '',
type:'2',
verified:true,
secret: '',
token: 'my_weixin_token_',
aesKey: ''
},
dataRule: {
name: [
{ required: true, message: '公众号名称不能为空', trigger: 'blur' }
],
appid: [
{ required: true, message: 'appid不能为空', trigger: 'blur' }
],
secret: [
{ required: true, message: 'appsecret不能为空', trigger: 'blur' }
]
}
}
},
computed: mapState({
ACCOUNT_TYPES: state=>state.wxAccount.ACCOUNT_TYPES
}),
methods: {
init (item) {
this.visible = true
if(item && item.appid){
this.dataForm = item
this.dataForm.type = item.type+''
}else{
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
}
<!-- 表单区域 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="100px">
<!-- 公众号名称输入 -->
<el-form-item label="公众号名称" prop="name">
<el-input v-model="dataForm.name" placeholder="公众号名称"></el-input>
</el-form-item>
<!-- 提示信息 -->
<div class="padding text-gray">测试号可选择服务号不同类型账号是否认证可使用功能权限不同<a href="https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.html">参考文档</a></div>
<el-row>
<el-col :span="12">
<!-- 公众号类型选择 -->
<el-form-item label="公众号类型" prop="type">
<el-select v-model="dataForm.type" placeholder="公众号类型">
<el-option v-for="(name,key) in ACCOUNT_TYPES" :key="name" :label="name" :value="key"></el-option>
</el-select>
</el-form-item>
</el-col>
},
//
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/manage/wxAccount/save`),
method: 'post',
data: this.$http.adornData(this.dataForm)
}).then(({data}) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<el-col :span="12">
<!-- 是否认证开关 -->
<el-form-item label="是否认证" prop="verified">
<el-switch v-model="dataForm.verified" placeholder="是否认证"></el-switch>
</el-form-item>
</el-col>
</el-row>
<!-- appid 输入 -->
<el-form-item label="appid" prop="appid">
<el-input v-model="dataForm.appid" placeholder="appid"></el-input>
</el-form-item>
<!-- appsecret 输入 -->
<el-form-item label="appsecret" prop="secret">
<el-input v-model="dataForm.secret" placeholder="appsecret"></el-input>
</el-form-item>
<!-- token 输入 -->
<el-form-item label="token" prop="token">
<el-input v-model="dataForm.token" placeholder="token"></el-input>
</el-form-item>
<!-- aesKey 输入 -->
<el-form-item label="aesKey" prop="aesKey">
<el-input v-model="dataForm.aesKey" placeholder="aesKey可为空"></el-input>
</el-form-item>
</el-form>
<!-- 对话框底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<script>
import { mapState } from 'vuex' // Vuex mapState
export default {
data () {
return {
visible: false, //
dataForm: { //
appid: '',
name: '',
type:'2', //
verified: true, // true
secret: '',
token: 'my_weixin_token_', // token
aesKey: '' // aesKey
},
dataRule: { //
name: [
{ required: true, message: '公众号名称不能为空', trigger: 'blur' } //
],
appid: [
{ required: true, message: 'appid不能为空', trigger: 'blur' } // appid
],
secret: [
{ required: true, message: 'appsecret不能为空', trigger: 'blur' } // appsecret
]
}
}
},
computed: mapState({
ACCOUNT_TYPES: state => state.wxAccount.ACCOUNT_TYPES // store
}),
methods: {
//
init (item) {
this.visible = true //
if(item && item.appid) {
this.dataForm = item //
this.dataForm.type = item.type + '' //
} else {
//
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
}
},
//
dataFormSubmit () {
//
this.$refs['dataForm'].validate((valid) => {
if (valid) {
// POST
this.$http({
url: this.$http.adornUrl(`/manage/wxAccount/save`), // URL
method: 'post',
data: this.$http.adornData(this.dataForm) // 使 adornData
}).then(({data}) => {
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false //
this.$emit('refreshDataList') //
}
})
} else {
//
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>

@ -1,68 +1,101 @@
<template>
<!-- 根据visible属性的值控制整个组件内容的显示与隐藏当visible为true时显示 -->
<div v-show="visible">
<!-- 定义一个表单绑定了dataForm数据模型使用了dataRule验证规则设置了表单尺寸为迷你型标签宽度为80px -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" size="mini" label-width="80px">
<!-- 第一行布局使用el-row包裹 -->
<el-row>
<!-- 占12列用于放置文章标题相关的表单元素 -->
<el-col :span="12">
<!-- 文章标题的表单项设置了必填验证对应dataForm中的title属性 -->
<el-form-item label="文章标题" prop="title" required>
<!-- 使用el-input组件实现输入框双向绑定dataForm.title设置了最大长度为1024有占位提示文字 -->
<el-input v-model="dataForm.title" :maxlength="1024" placeholder="标题"></el-input>
</el-form-item>
</el-col>
<!-- 同样占12列用于放置文章类型相关的表单元素 -->
<el-col :span="12">
<!-- 文章类型的表单项设置了必填验证对应dataForm中的type属性 -->
<el-form-item label="文章类型" prop="type" required>
<!-- 使用el-select组件实现下拉选择框双向绑定dataForm.type有占位提示文字 -->
<el-select v-model="dataForm.type" placeholder="选择文章类型">
<!-- 循环遍历ARTICLE_TYPES对象生成下拉选项选项的标签显示为name值为key允许用户创建新的选项 -->
<el-option v-for="(name,key) in ARTICLE_TYPES" :key="name" :label="name" :value="key" allow-create></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行布局 -->
<el-row>
<!-- 占12列用于放置一级目录相关的表单元素 -->
<el-col :span="12">
<!-- 一级目录的表单项对应dataForm中的category属性 -->
<el-form-item label="一级目录" prop="category">
<!-- 使用el-input组件实现输入框双向绑定dataForm.category设置了最大长度为50有占位提示文字 -->
<el-input :maxlength="50" v-model="dataForm.category" placeholder="一级目录"></el-input>
</el-form-item>
</el-col>
<!-- 占12列用于放置二级分类相关的表单元素 -->
<el-col :span="12">
<!-- 二级分类的表单项对应dataForm中的subCategory属性 -->
<el-form-item label="二级分类" prop="subCategory">
<!-- 使用el-input组件实现输入框双向绑定dataForm.subCategory设置了最大长度为50有占位提示文字 -->
<el-input :maxlength="50" v-model="dataForm.subCategory" placeholder="二级目录"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 指向外链的表单项对应dataForm中的targetLink属性 -->
<el-form-item label="指向外链" prop="targetLink">
<!-- 使用el-input组件实现输入框双向绑定dataForm.targetLink有占位提示文字 -->
<el-input v-model="dataForm.targetLink" placeholder="指向外链"></el-input>
</el-form-item>
<!-- 摘要的表单项对应dataForm中的summary属性 -->
<el-form-item label="摘要" prop="summary">
<!-- 使用el-input组件实现文本域输入框双向绑定dataForm.summary设置了行数为3最大长度为512显示字数限制提示 -->
<el-input v-model="dataForm.summary" placeholder="摘要" type="textarea" rows="3" maxlength="512" show-word-limit></el-input>
</el-form-item>
<!-- 标签的表单项使用了自定义的tags-editor组件双向绑定dataForm.tags -->
<el-form-item label="标签" prop="tags">
<tags-editor v-model="dataForm.tags"></tags-editor>
</el-form-item>
<!-- 封面图的表单项对应dataForm中的image属性 -->
<el-form-item label="封面图" prop="image">
<!-- 使用el-input组件实现输入框双向绑定dataForm.image有占位提示文字 -->
<el-input v-model="dataForm.image" placeholder="图片链接">
<!-- 插入一个自定义的OssUploader组件用于上传图片上传成功后将返回值赋给dataForm.image -->
<OssUploader slot="append" @uploaded="dataForm.image=$event"></OssUploader>
</el-input>
</el-form-item>
<!-- 引入一个富文本编辑器组件双向绑定dataForm.content -->
<tinymce-editor ref="editor" v-model="dataForm.content"></tinymce-editor>
</el-form>
<!-- 距离顶部有一定间距靠右对齐的按钮区域 -->
<div class="margin-top text-right">
<!-- 取消按钮点击时通过$emit触发父组件的hide事件 -->
<el-button @click="$emit('hide')"></el-button>
<!-- 确定按钮类型为主要按钮点击时调用dataFormSubmit方法进行表单提交 -->
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</div>
</div>
</template>
<script>
// VuexmapStateVuex
import { mapState } from 'vuex'
export default {
name:'article-add-or-update',
name: 'article-add-or-update',
components: {
// TinymceEditor
TinymceEditor: () => import("@/components/tinymce-editor"),
// tags-editor
tagsEditor: () => import("@/components/tags-editor"),
// OssUploaderOSS
OssUploader: () => import('../oss/oss-uploader')
},
props:{
visible:{
type:Boolean,
default:false
props: {
visible: {
type: Boolean,
default: false
}
},
data() {
@ -94,37 +127,45 @@ export default {
};
},
computed: mapState({
ARTICLE_TYPES: state=>state.article.ARTICLE_TYPES
// VuexstatearticleARTICLE_TYPES
ARTICLE_TYPES: state => state.article.ARTICLE_TYPES
}),
methods: {
init(id) {
// dataFormidid使
this.dataForm.id = id || "";
this.$nextTick(() => {
// DOM
this.$refs["dataForm"].resetFields();
if (id > 0) {
// id0HTTP
this.$http({
url: this.$http.adornUrl(`/manage/article/info/${this.dataForm.id}`),
method: "get",
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataForm=data.article;
// dataForm
this.dataForm = data.article;
this.dataForm.type = data.article.type + "";
}
});
}
});
},
//
//
dataFormSubmit() {
// valid
this.$refs["dataForm"].validate(valid => {
if (valid) {
// HTTP
this.$http({
url: this.$http.adornUrl(`/manage/article/save`),
method: "post",
data: this.$http.adornData(this.dataForm)
}).then(({ data }) => {
if (data && data.code === 200) {
// $emitrefreshDataListhide
this.$message({
message: "操作成功",
type: "success",
@ -135,6 +176,7 @@ export default {
}
});
} else {
//
this.$message.error(data.msg);
}
});
@ -144,12 +186,14 @@ export default {
imgUploadSuccess(response, file, fileList) {
console.log(response);
if (response.code == 200) {
// dataForm.image
this.dataForm.image = response.data;
console.log("this.article", this.article);
} else {
//
this.$message.warning(response.msg);
}
}
}
};
</script>
</script>

@ -1,37 +1,56 @@
<template>
<!-- 外层容器div -->
<div>
<!-- 根据条件控制显示内容当addOrUpdateVisible为false时显示以下部分 -->
<div v-show="!addOrUpdateVisible">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 文章类型选择的表单项 -->
<el-form-item>
<!-- 使用el-select组件实现下拉选择框双向绑定dataForm.type有占位提示文字选项通过循环ARTICLE_TYPES生成 -->
<el-select v-model="dataForm.type" placeholder="选择文章类型">
<el-option v-for="(name,key) in ARTICLE_TYPES" :key="key" :label="name" :value="key" allow-create></el-option>
</el-select>
</el-form-item>
<!-- 文章标题输入的表单项使用el-input组件双向绑定dataForm.title有占位提示文字且可清空输入内容 -->
<el-form-item>
<el-input v-model="dataForm.title" placeholder="标题" clearable></el-input>
</el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item>
<!-- 查询按钮点击时先将当前页码设置为1然后调用getDataList方法获取数据 -->
<el-button @click="pageIndex=1;getDataList()"></el-button>
<!-- 新增按钮根据权限调用isAuth方法判断决定是否显示类型为主要按钮点击时调用addOrUpdateHandle方法 -->
<el-button v-if="isAuth('wx:article:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:article:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<!-- ID列对应dataList中数据项的id属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="id" header-align="center" align="center" label="ID">
</el-table-column>
<!-- 文章类型列对应dataList中数据项的type属性使用formatter函数格式化显示内容设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="type" header-align="center" align="center" :formatter="articleTypeFormat" label="文章类型">
</el-table-column>
<!-- 标题列对应dataList中数据项的title属性显示溢出提示设置了表头和内容的对齐方式以及列标题内容用a标签包裹可点击跳转 -->
<el-table-column prop="title" header-align="center" align="center" show-overflow-tooltip label="标题">
<a :href="scope.row.targetLink" slot-scope="scope">{{scope.row.title}}</a>
</el-table-column>
<!-- 一级分类列对应dataList中数据项的category属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="category" header-align="center" align="center" label="一级分类">
</el-table-column>
<!-- 二级分类列对应dataList中数据项的subCategory属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="subCategory" header-align="center" align="center" label="二级分类">
</el-table-column>
<!-- 打开次数列对应dataList中数据项的openCount属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="openCount" header-align="center" align="center" label="打开次数">
</el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button>
@ -39,16 +58,19 @@
</template>
</el-table-column>
</el-table>
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</div>
<!-- 新增 / 修改 -->
<add-or-update :visible="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList" @hide="addOrUpdateVisible=false"></add-or-update>
<!-- 新增/修改组件通过属性和事件与父组件进行交互 -->
<add-or-update :visible="addOrUpdateVisible" ref="addOr-update" @refreshDataList="getDataList" @hide="addOrUpdateVisible=false"></add-or-update>
</div>
</template>
<script>
// /
import AddOrUpdate from './article-add-or-update'
// VuexmapStateVuex
import { mapState } from 'vuex'
export default {
components: {
@ -70,15 +92,19 @@ export default {
}
},
computed: mapState({
ARTICLE_TYPES: state=>state.article.ARTICLE_TYPES
// VuexstatearticleARTICLE_TYPES
ARTICLE_TYPES: state => state.article.ARTICLE_TYPES
}),
mounted() {
// getDataList
this.getDataList()
},
methods: {
//
//
getDataList() {
// true
this.dataListLoading = true
// HTTPURL
this.$http({
url: this.$http.adornUrl('/manage/article/list'),
method: 'get',
@ -91,52 +117,60 @@ export default {
'order': 'desc'
})
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.dataList = data.page.list
this.totalCount = data.page.totalCount
} else {
//
this.dataList = []
this.totalCount = 0
}
// false
this.dataListLoading = false
})
},
//
// 1
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
//
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
//
selectionChangeHandle(val) {
this.dataListSelections = val
},
// /
// //trueDOM/id
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
//
// idHTTP
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.id)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
// id使id
var ids = id? [id] : this.dataListSelections.map(item => item.id)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/article/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
@ -144,14 +178,16 @@ export default {
onClose: () => this.getDataList()
})
} else {
//
this.$message.error(data.msg)
}
})
})
},
// ARTICLE_TYPES
articleTypeFormat(row, column, cellValue) {
return this.ARTICLE_TYPES[cellValue];
}
}
}
</script>
</script>

@ -1,38 +1,44 @@
<template>
<el-dialog title="选择素材" :visible.sync="dataVisible" :modal="true" append-to-body @close="onClose">
<material-news v-if="selectType=='news'" @selected="onSelect" selectMode></material-news>
<material-file v-else :fileType="selectType" @selected="onSelect" selectMode></material-file>
</el-dialog>
</template>
<script>
export default {
name:"assets-selector",
data:function (){
return {
dataVisible : this.visible
}
},
components:{
MaterialFile:()=>import('./material-file'),
MaterialNews:()=>import('./material-news')
},
props:{
selectType:{// imagevoicevideonews
type:String,
default:'image'
},
visible:{
type:Boolean,
default:false
}
},
methods:{
onSelect(itemInfo){
this.$emit('selected', itemInfo)
},
onClose(){
this.$emit('onClose')
}
}
}
<template>
<!-- 选择素材的对话框 -->
<el-dialog title="选择素材" :visible.sync="dataVisible" :modal="true" append-to-body @close="onClose">
<!-- 根据选择的素材类型加载不同的子组件 -->
<material-news v-if="selectType === 'news'" @selected="onSelect" selectMode></material-news>
<material-file v-else :fileType="selectType" @selected="onSelect" selectMode></material-file>
</el-dialog>
</template>
<script>
export default {
name: "assets-selector", //
data: function () {
return {
dataVisible: this.visible // dataVisible visible
}
},
components: {
//
MaterialFile: () => import('./material-file'), // MaterialFile
MaterialNews: () => import('./material-news') // MaterialNews
},
props: {
selectType: { // imagevoicevideonews
type: String,
default: 'image' // 'image'
},
visible: { //
type: Boolean,
default: false //
}
},
methods: {
//
onSelect(itemInfo) {
this.$emit('selected', itemInfo) //
},
//
onClose() {
this.$emit('onClose') //
}
}
}
</script>

@ -1,103 +1,118 @@
<template>
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="媒体文件">
<el-button type="primary">
选择文件
<input type="file" style="opacity: 0;height: 100%;position: absolute;left: 0;top: 0;" @change="onFileChange" />
</el-button>
<div>{{dataForm.file.name}}</div>
</el-form-item>
<el-form-item label="媒体类型" prop="mediaType">
<el-select v-model="dataForm.mediaType" placeholder="媒体类型" style="width:100%">
<el-option label="图片2M以内支持PNG\JPEG\JPG\GIF" value="image"></el-option>
<el-option label="视频10M以内只支持MP4" value="video"></el-option>
<el-option label="语音2M、60s以内支持AMR\MP3" value="voice"></el-option>
<el-option label="缩略图64K以内JPG" value="thumb"></el-option>
</el-select>
</el-form-item>
<el-form-item label="素材名称" prop="fileName">
<el-input v-model="dataForm.fileName" placeholder="为便于管理建议按用途分类+素材内容命名"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{uploading?'提交中...':'提交'}}</el-button>
</span>
</el-dialog>
</template>
<template>
<!-- 对话框标题根据是否有 id 决定是新增还是修改 -->
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<!-- 表单区域 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<!-- 媒体文件选择 -->
<el-form-item label="媒体文件">
<el-button type="primary">
选择文件
<!-- 隐藏的文件输入框选择文件后触发 onFileChange 方法 -->
<input type="file" style="opacity: 0; height: 100%; position: absolute; left: 0; top: 0;" @change="onFileChange" />
</el-button>
<!-- 显示已选择的文件名称 -->
<div>{{dataForm.file.name}}</div>
</el-form-item>
<!-- 媒体类型选择 -->
<el-form-item label="媒体类型" prop="mediaType">
<el-select v-model="dataForm.mediaType" placeholder="媒体类型" style="width:100%">
<el-option label="图片2M以内支持PNG\JPEG\JPG\GIF" value="image"></el-option>
<el-option label="视频10M以内只支持MP4" value="video"></el-option>
<el-option label="语音2M、60s以内支持AMR\MP3" value="voice"></el-option>
<el-option label="缩略图64K以内JPG" value="thumb"></el-option>
</el-select>
</el-form-item>
<!-- 素材名称输入 -->
<el-form-item label="素材名称" prop="fileName">
<el-input v-model="dataForm.fileName" placeholder="为便于管理建议按用途分类+素材内容命名"></el-input>
</el-form-item>
</el-form>
<!-- 对话框底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{uploading ? '提交中...' : '提交'}}</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data() {
return {
visible: false,
uploading:false,
dataForm: {
mediaId: '',
file: '',
fileName: '',
mediaType: 'image'
},
dataRule: {
fileName: [
{ required: true, message: '素材名称不能为空', trigger: 'blur' }
],
mediaType: [
{ required: true, message: '素材类型不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init(fileType) {
if(fileType)this.dataForm.mediaType=fileType
this.visible = true
},
//
dataFormSubmit() {
if(this.uploading)return
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.uploading=true
console.log(this.dataForm)
let form = new FormData();
form.append('mediaId', this.dataForm.mediaId || '')
form.append('file', this.dataForm.file)
form.append('fileName', this.dataForm.fileName)
form.append('mediaType', this.dataForm.mediaType)
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialFileUpload`),
method: 'post',
data: form,
headers: { 'Content-Type': 'multipart/form-data' }
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
this.uploading=false
})
}
})
},
onFileChange(e) {
let file = event.currentTarget.files[0]
this.dataForm.file = file;
this.dataForm.fileName = file.name.substring(0, file.name.lastIndexOf('.'))
let mediaType = file.type.substring(0, file.type.lastIndexOf('/'))
if (mediaType == 'audio') mediaType = 'voice'
this.dataForm.mediaType = mediaType
}
}
}
</script>
<script>
export default {
data() {
return {
visible: false, //
uploading: false, //
dataForm: { //
mediaId: '', // ID
file: '', //
fileName: '', //
mediaType: 'image' // 'image'
},
dataRule: { //
fileName: [
{ required: true, message: '素材名称不能为空', trigger: 'blur' } //
],
mediaType: [
{ required: true, message: '素材类型不能为空', trigger: 'blur' } //
]
}
}
},
methods: {
//
init(fileType) {
if (fileType) this.dataForm.mediaType = fileType //
this.visible = true //
},
//
dataFormSubmit() {
if (this.uploading) return //
//
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.uploading = true //
console.log(this.dataForm) //
let form = new FormData(); // FormData
// FormData
form.append('mediaId', this.dataForm.mediaId || '')
form.append('file', this.dataForm.file)
form.append('fileName', this.dataForm.fileName)
form.append('mediaType', this.dataForm.mediaType)
// POST
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialFileUpload`), // URL
method: 'post',
data: form,
headers: { 'Content-Type': 'multipart/form-data' } // multipart/form-data
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false //
this.$emit('refreshDataList') //
}
})
} else {
//
this.$message.error(data.msg)
}
this.uploading = false //
})
}
})
},
//
onFileChange(e) {
let file = e.currentTarget.files[0] //
this.dataForm.file = file; //
this.dataForm.fileName = file.name.substring(0, file.name.lastIndexOf('.')) //
let mediaType = file.type.substring(0, file.type.lastIndexOf('/')) //
if (mediaType === 'audio') mediaType = 'voice' // 'voice'
this.dataForm.mediaType = mediaType //
}
}
}
</script>

@ -1,185 +1,207 @@
<template>
<div class="mod-menu">
<el-form :inline="true" :model="dataForm">
<el-form-item v-show="!selectMode">
<el-button size="mini" v-if="isAuth('wx:wxassets:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
</el-form-item>
</el-form>
<div v-loading="dataListLoading">
<div class="card" v-for="item in dataList" :key="item.mediaId" @click="onSelect(item)">
<el-image v-if="fileType=='image'" class="card-image" :src="item.url" fit="contain" lazy></el-image>
<div v-else class="card-preview">
<div v-if="fileType=='voice'" class="card-preview-icon el-icon-microphone"></div>
<div v-if="fileType=='video'" class="card-preview-icon el-icon-video-camera-solid"></div>
<div class="card-preview-text">管理后台不支持预览<br/>微信中可正常播放</div>
</div>
<div class="card-footer">
<div class="text-cut-name">{{item.name}}</div>
<div>{{$moment(item.updateTime).calendar()}}</div>
<div class="flex justify-between align-center" v-show="!selectMode">
<el-button size="mini" type="text" icon="el-icon-copy-document" v-clipboard:copy="item.mediaId" v-clipboard:success="onCopySuccess" v-clipboard:error="onCopyError">复制media_id</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="deleteHandle(item.mediaId)" >删除</el-button>
</div>
</div>
</div>
</div>
<el-pagination @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[20]" :page-size="20" :total="totalCount" layout="total, prev,pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="onChange"></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from './material-file-add-or-update'
export default {
name:'material-file',
props:{
fileType:{// imagevoicevideo
type:String,
default:'image'
},
selectMode:{//
type:Boolean,
default:false
}
},
components: {
AddOrUpdate
},
data() {
return {
dataForm: {},
addOrUpdateVisible: false,
dataList: [],
pageIndex: 1,
pageSize: 20,
totalCount: 0,
dataListLoading: false,
}
},
mounted(){
this.init()
},
methods: {
init(){
if(!this.dataList.length){
this.getDataList()
}
},
getDataList() {
if(this.dataListLoading) return
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialFileBatchGet'),
params: this.$http.adornParams({
'page': this.pageIndex,
'type': this.fileType
})
}).then(({ data }) => {
if (data && data.code == 200) {
this.dataList = data.data.items
this.totalCount = data.data.totalCount
this.pageIndex++;
} else {
this.$message.error(data.msg);
}
this.dataListLoading = false
})
},
// /
addOrUpdateHandle() {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(this.fileType)
})
},
onSelect(itemInfo){
if(!this.selectMode)return
this.$emit('selected',itemInfo)
},
//
deleteHandle(id) {
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'),
method: 'post',
data: { mediaId: id }
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.onChange()
})
} else {
this.$message.error(data.msg)
}
})
})
},
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
onCopySuccess(){
this.$message.success('已复制')
},
onCopyError(err){
this.$message.error('复制失败,可能是此浏览器不支持复制')
},
onChange(){
this.pageIndex=1
this.getDataList()
this.$emit('change')
}
<template>
<div class="mod-menu">
<!-- 表单用于新增按钮 -->
<el-form :inline="true" :model="dataForm">
<el-form-item v-show="!selectMode">
<!-- 新增按钮只有在有权限时才显示 -->
<el-button size="mini" v-if="isAuth('wx:wxassets:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
</el-form-item>
</el-form>
<!-- 数据列表加载状态 -->
<div v-loading="dataListLoading">
<!-- 遍历数据列表生成每个素材的卡片 -->
<div class="card" v-for="item in dataList" :key="item.mediaId" @click="onSelect(item)">
<!-- 如果是图片类型显示图片 -->
<el-image v-if="fileType === 'image'" class="card-image" :src="item.url" fit="contain" lazy></el-image>
<div v-else class="card-preview">
<!-- 根据不同的媒体类型显示不同的图标和提示 -->
<div v-if="fileType === 'voice'" class="card-preview-icon el-icon-microphone"></div>
<div v-if="fileType === 'video'" class="card-preview-icon el-icon-video-camera-solid"></div>
<div class="card-preview-text">管理后台不支持预览<br/>微信中可正常播放</div>
</div>
<div class="card-footer">
<!-- 显示素材名称和更新时间 -->
<div class="text-cut-name">{{item.name}}</div>
<div>{{$moment(item.updateTime).calendar()}}</div>
<div class="flex justify-between align-center" v-show="!selectMode">
<!-- 复制 media_id 的按钮 -->
<el-button size="mini" type="text" icon="el-icon-copy-document" v-clipboard:copy="item.mediaId" v-clipboard:success="onCopySuccess" v-clipboard:error="onCopyError">复制media_id</el-button>
<!-- 删除素材的按钮 -->
<el-button size="mini" type="text" icon="el-icon-delete" @click="deleteHandle(item.mediaId)"></el-button>
</div>
</div>
</div>
</div>
<!-- 分页组件 -->
<el-pagination @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[20]" :page-size="20" :total="totalCount" layout="total, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗用于新增/修改素材 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="onChange"></add-or-update>
</div>
</template>
}
}
</script>
<style scoped>
.card{
width: 170px;
display: inline-block;
background: #FFFFFF;
border: 1px solid #EBEEF5;
box-shadow: 1px 1px 20px 0 rgba(0, 0, 0, 0.1);
margin: 0 10px 10px 0;
vertical-align: top;
border-radius: 5px;
box-sizing: border-box;
}
.card:hover{
border: 2px solid #66b1ff;
margin-bottom: 6px;
}
.card-image{
line-height: 170px;
max-height: 170px;
width: 100%;
}
.card-preview{
padding: 20px 0;
color: #d9d9d9;
display: flex;
justify-content: center;
align-items: center;
}
.card-preview-icon{
font-size: 30px;
margin-right: 5px;
}
.card-preview-text{
font-size: 12px;
}
.card-footer{
color: #ccc;
font-size: 12px;
padding: 15px 10px;
}
<script>
import AddOrUpdate from './material-file-add-or-update' // /
export default {
name: 'material-file', //
props: {
fileType: { // imagevoicevideo
type: String,
default: 'image'
},
selectMode: { //
type: Boolean,
default: false
}
},
components: {
AddOrUpdate // /
},
data() {
return {
dataForm: {}, //
addOrUpdateVisible: false, // /
dataList: [], //
pageIndex: 1, //
pageSize: 20, //
totalCount: 0, //
dataListLoading: false, //
}
},
mounted() {
this.init() //
},
methods: {
init() {
//
if (!this.dataList.length) {
this.getDataList()
}
},
getDataList() {
//
if (this.dataListLoading) return //
this.dataListLoading = true //
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialFileBatchGet'), // URL
params: this.$http.adornParams({
'page': this.pageIndex, //
'type': this.fileType //
})
}).then(({ data }) => {
if (data && data.code == 200) {
//
this.dataList = data.data.items
this.totalCount = data.data.totalCount
this.pageIndex++; //
} else {
//
this.$message.error(data.msg);
}
this.dataListLoading = false //
})
},
// /
addOrUpdateHandle() {
this.addOrUpdateVisible = true // /
this.$nextTick(() => {
this.$refs.addOrUpdate.init(this.fileType) // /
})
},
onSelect(itemInfo) {
//
if (!this.selectMode) return //
this.$emit('selected', itemInfo) //
},
//
deleteHandle(id) {
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'), // URL
method: 'post',
data: { mediaId: id } // ID
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.onChange() //
})
} else {
//
this.$message.error(data.msg)
}
})
})
},
//
currentChangeHandle(val) {
this.pageIndex = val //
this.getDataList() //
},
onCopySuccess() {
//
this.$message.success('已复制')
},
onCopyError(err) {
//
this.$message.error('复制失败, 可能是此浏览器不支持复制')
},
onChange() {
//
this.pageIndex = 1 // 1
this.getDataList() //
this.$emit('change') //
}
}
}
</script>
<style scoped>
.card {
width: 170px; /* 卡片宽度 */
display: inline-block; /* 以行内块元素方式显示,允许多个卡片横向排列 */
background: #FFFFFF; /* 背景颜色 */
border: 1px solid #EBEEF5; /* 边框颜色 */
box-shadow: 1px 1px 20px 0 rgba(0, 0, 0, 0.1); /* 盒子阴影 */
margin: 0 10px 10px 0; /* 外边距 */
vertical-align: top; /* 垂直对齐 */
border-radius: 5px; /* 圆角 */
box-sizing: border-box; /* 包含内边距和边框在内的盒模型 */
}
.card:hover {
border: 2px solid #66b1ff; /* 鼠标悬停时加粗边框 */
margin-bottom: 6px; /* 下方外边距调整 */
}
.card-image {
line-height: 170px; /* 设定行高以居中内容 */
max-height: 170px; /* 最大高度 */
width: 100%; /* 宽度100% */
}
.card-preview {
padding: 20px 0; /* 上下内边距 */
color: #d9d9d9; /* 字体颜色 */
display: flex; /* 使用弹性布局 */
justify-content: center; /* 内容居中对齐 */
align-items: center; /* 垂直居中对齐 */
}
.card-preview-icon {
font-size: 30px; /* 图标字体大小 */
margin-right: 5px; /* 右侧外边距 */
}
.card-preview-text {
font-size: 12px; /* 文本大小 */
}
.card-footer {
color: #ccc; /* 字体颜色 */
font-size: 12px; /* 字体大小 */
padding: 15px 10px; /* 内边距 */
}
</style>

@ -1,221 +1,232 @@
<template>
<div v-show="visible">
<div class="flex">
<div class="card-list">
<div class="text-center margin-bottom">图文列表</div>
<div class="card-item" :class="{'selected':selectedIndex==index}" v-for="(item,index) in articles" :key="index" @click="selectedIndex=index">
<div class="text-cut-name">{{item.title}}</div>
</div>
<div v-show="articles.length<8 && !mediaId" class="card-add el-icon-plus" @click="addArticle()"></div>
</div>
<el-form size="mini" v-if="articles.length" :model="articles[selectedIndex]" :rules="dataRule" ref="dataForm" label-width="100px">
<el-form-item label="标题" prop="title">
<el-input v-model="articles[selectedIndex].title" placeholder="标题"></el-input>
</el-form-item>
<el-form-item label="封面图" prop="thumbMediaId">
<el-input v-model="articles[selectedIndex].thumbMediaId" placeholder="封面图media_id">
<div slot="append" @click="assetsSelectorVisible=true"></div>
</el-input>
</el-form-item>
<el-form-item label="摘要" prop="digest">
<el-input v-model="articles[selectedIndex].digest" placeholder="摘要"></el-input>
</el-form-item>
<el-form-item label="原文地址" prop="contentSourceUrl">
<el-input v-model="articles[selectedIndex].contentSourceUrl" placeholder="阅读原文链接"></el-input>
</el-form-item>
<el-row>
<el-col :span="9">
<el-form-item label="作者" prop="author">
<el-input v-model="articles[selectedIndex].author" placeholder="作者"></el-input>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="显示封面" prop="showCoverPic">
<el-switch v-model="articles[selectedIndex].showCoverPic"></el-switch>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="允许评论" prop="needOpenComment">
<el-switch v-model="articles[selectedIndex].needOpenComment"></el-switch>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="仅粉丝可评论" prop="onlyFansCanComment">
<el-switch v-model="articles[selectedIndex].onlyFansCanComment"></el-switch>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="内容" prop="content">
<tinymce-editor ref="editor" v-model="articles[selectedIndex].content"> </tinymce-editor>
</el-form-item>
</el-form>
</div>
<div class="dialog-footer">
<el-button @click="$emit('hide')"></el-button>
<el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{this.mediaId?'修改此篇':'全部提交(共'+articles.length+'篇)'}}</el-button>
</div>
<assets-selector v-if="assetsSelectorVisible" :visible="assetsSelectorVisible" selectType="image" @selected="onAssetsSelect"></assets-selector>
</div>
</template>
<template>
<div v-show="visible"> <!-- -->
<div class="flex">
<div class="card-list">
<div class="text-center margin-bottom">图文列表</div>
<!-- 遍历所有文章生成列表项 -->
<div class="card-item" :class="{'selected':selectedIndex==index}" v-for="(item,index) in articles" :key="index" @click="selectedIndex=index">
<div class="text-cut-name">{{item.title}}</div>
</div>
<!-- 如果文章少于8篇且没有媒体ID显示新增文章的按钮 -->
<div v-show="articles.length<8 && !mediaId" class="card-add el-icon-plus" @click="addArticle()"></div>
</div>
<!-- 文章表单绑定到选中的文章 -->
<el-form size="mini" v-if="articles.length" :model="articles[selectedIndex]" :rules="dataRule" ref="dataForm" label-width="100px">
<el-form-item label="标题" prop="title">
<el-input v-model="articles[selectedIndex].title" placeholder="标题"></el-input>
</el-form-item>
<el-form-item label="封面图" prop="thumbMediaId">
<el-input v-model="articles[selectedIndex].thumbMediaId" placeholder="封面图media_id">
<div slot="append" @click="assetsSelectorVisible=true"></div> <!-- -->
</el-input>
</el-form-item>
<el-form-item label="摘要" prop="digest">
<el-input v-model="articles[selectedIndex].digest" placeholder="摘要"></el-input>
</el-form-item>
<el-form-item label="原文地址" prop="contentSourceUrl">
<el-input v-model="articles[selectedIndex].contentSourceUrl" placeholder="阅读原文链接"></el-input>
</el-form-item>
<el-row>
<el-col :span="9">
<el-form-item label="作者" prop="author">
<el-input v-model="articles[selectedIndex].author" placeholder="作者"></el-input>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="显示封面" prop="showCoverPic">
<el-switch v-model="articles[selectedIndex].showCoverPic"></el-switch>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="允许评论" prop="needOpenComment">
<el-switch v-model="articles[selectedIndex].needOpenComment"></el-switch>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="仅粉丝可评论" prop="onlyFansCanComment">
<el-switch v-model="articles[selectedIndex].onlyFansCanComment"></el-switch>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="内容" prop="content">
<tinymce-editor ref="editor" v-model="articles[selectedIndex].content"> </tinymce-editor> <!-- 富文本编辑器 -->
</el-form-item>
</el-form>
</div>
<div class="dialog-footer">
<el-button @click="$emit('hide')"></el-button> <!-- 取消按钮 -->
<el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{this.mediaId?'修改此篇':'全部提交(共'+articles.length+'篇)'}}</el-button>
<!-- 提交按钮根据是否有mediaId来决定按钮显示内容 -->
</div>
<assets-selector v-if="assetsSelectorVisible" :visible="assetsSelectorVisible" selectType="image" @selected="onAssetsSelect"></assets-selector> <!-- 选择素材框 -->
</div>
</template>
<script>
const articleTemplate={
templateId: 0,
title: '',
content: '',
author: '',
showCoverPic: true,
contentSourceUrl: '',
digest: '',
thumbMediaId: '',
needOpenComment: false,
onlyFansCanComment: false
}
export default {
components: {
TinymceEditor: () => import('@/components/tinymce-editor'),
AssetsSelector:()=>import('./assets-selector')
},
props:{
visible:{
type:Boolean,
default:false
}
},
data() {
return {
assetsSelectorVisible:false,
mediaId:'',
selectedIndex:0,
articles:[],
uploading:false,
dataRule: {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
content: [
{ required: true, message: '内容不能为空', trigger: 'blur' }
],
thumbMediaId: [
{ required: true, message: '封面图media_id不能为空', trigger: 'blur' }
],
contentSourceUrl: [
{ required: true, message: '原文地址不得为空', trigger: 'blur' }
]
}
}
},
methods: {
init(news){
if(news && news.mediaId){
this.mediaId=news.mediaId
this.articles = news.content.articles
}else{
this.mediaId=''
this.articles=[{...articleTemplate}]
}
},
//
dataFormSubmit() {
if(this.uploading)return
this.$refs['dataForm'].validate((valid) => {
if (valid) {
if(this.mediaId){//
this.materialArticleUpdate();
}else{ //
this.materialNewsUpload();
}
}
})
},
materialNewsUpload(){
this.uploading=true
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialNewsUpload`),
method: 'post',
data: this.$http.adornData(this.articles,false)
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.$emit("refreshDataList");
this.emit('hide')
}
});
} else {
this.$message.error(data.msg)
}
this.uploading=false
})
},
materialArticleUpdate(){
this.uploading=true
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialArticleUpdate`),
method: 'post',
data: this.$http.adornData({
'mediaId':this.mediaId,
'index':this.selectedIndex,
'articles':this.articles[this.selectedIndex]
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message.success('操作成功')
} else {
this.$message.error(data.msg)
}
this.uploading=false
})
},
addArticle(){
this.articles.push({...articleTemplate})
this.selectedIndex=this.articles.length-1
},
onAssetsSelect(assetsInfo){
Vue.set(this.articles[this.selectedIndex], 'thumbMediaId', assetsInfo.mediaId)
this.assetsSelectorVisible=false
}
}
}
</script>
<style scoped>
.card-list{
width: 300px;
padding-right: 10px;
border-right: 1px solid #eeeeee;
}
.card-item{
margin-top: 2px;
padding: 20px 5px;
border: 1px solid #ddd;
font-size: 12px;
line-height: 15px;
}
.card-item.selected{
border: 2px solid #409EFF;
}
.text-cut-name{
display: -webkit-box;
word-wrap:break-word;
word-break:break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.card-add{
margin-top: 2px;
display: block;
border: 1px dotted #ddd;
color: #ddd;
text-align: center;
font-size: 30px;
line-height: 50px;
}
.dialog-footer {
margin-top: 20px;
text-align: right;
}
<script>
const articleTemplate = { //
templateId: 0,
title: '',
content: '',
author: '',
showCoverPic: true,
contentSourceUrl: '',
digest: '',
thumbMediaId: '',
needOpenComment: false,
onlyFansCanComment: false
}
export default {
components: {
TinymceEditor: () => import('@/components/tinymce-editor'), //
AssetsSelector: () => import('./assets-selector') //
},
props: {
visible: { //
type: Boolean,
default: false
}
},
data() {
return {
assetsSelectorVisible: false, //
mediaId: '', // ID
selectedIndex: 0, //
articles: [], //
uploading: false, //
dataRule: { //
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
content: [
{ required: true, message: '内容不能为空', trigger: 'blur' }
],
thumbMediaId: [
{ required: true, message: '封面图media_id不能为空', trigger: 'blur' }
],
contentSourceUrl: [
{ required: true, message: '原文地址不得为空', trigger: 'blur' }
]
}
}
},
methods: {
init(news) { //
if (news && news.mediaId) {
this.mediaId = news.mediaId; // mediaIdmediaId
this.articles = news.content.articles; //
} else {
this.mediaId = ''; //
this.articles = [{ ...articleTemplate }]; // 使
}
},
//
dataFormSubmit() {
if (this.uploading) return; //
this.$refs['dataForm'].validate((valid) => { //
if (valid) {
if (this.mediaId) { // mediaId
this.materialArticleUpdate();
} else { //
this.materialNewsUpload();
}
}
});
},
//
materialNewsUpload() {
this.uploading = true; // true
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialNewsUpload`), // URL
method: 'post', //
data: this.$http.adornData(this.articles, false) //
}).then(({ data }) => {
//
if (data && data.code === 200) {
this.$message({
message: "操作成功", //
type: "success",
duration: 1500,
onClose: () => {
this.$emit("refreshDataList"); //
this.$emit('hide'); //
}
});
} else {
this.$message.error(data.msg); //
}
this.uploading = false; //
});
},
//
materialArticleUpdate() {
this.uploading = true; // true
this.$http({
url: this.$http.adornUrl(`/manage/wxAssets/materialArticleUpdate`), // URL
method: 'post', //
data: this.$http.adornData({
'mediaId': this.mediaId, // mediaId
'index': this.selectedIndex, //
'articles': this.articles[this.selectedIndex] //
})
}).then(({ data }) => {
//
if (data && data.code === 200) {
this.$message.success('操作成功'); //
} else {
this.$message.error(data.msg); //
}
this.uploading = false; //
});
},
//
addArticle() {
this.articles.push({ ...articleTemplate }); //
this.selectedIndex = this.articles.length - 1; //
},
//
onAssetsSelect(assetsInfo) {
Vue.set(this.articles[this.selectedIndex], 'thumbMediaId', assetsInfo.mediaId); // mediaId
this.assetsSelectorVisible = false; //
}
}
}
</script>
<style scoped>
.card-list {
width: 300px; /* 卡片列表的宽度 */
padding-right: 10px; /* 右侧内边距 */
border-right: 1px solid #eeeeee; /* 右侧边框 */
}
.card-item {
margin-top: 2px; /* 上外边距 */
padding: 20px 5px; /* 内边距 */
border: 1px solid #ddd; /* 边框 */
font-size: 12px; /* 字体大小 */
line-height: 15px; /* 行高 */
}
.card-item.selected {
border: 2px solid #409EFF; /* 选中时的边框颜色 */
}
.text-cut-name {
display: -webkit-box; /* 使用盒子模型 */
word-wrap: break-word; /* 自动换行 */
word-break: break-all; /* 强制断行 */
-webkit-box-orient: vertical; /* 垂直方向的盒子模型 */
-webkit-line-clamp: 2; /* 限制显示的行数 */
overflow: hidden; /* 溢出隐藏 */
}
.card-add {
margin-top: 2px; /* 上外边距 */
display: block; /* 块级元素 */
border: 1px dotted #ddd; /* 虚线边框 */
color: #ddd; /* 文字颜色 */
text-align: center; /* 居中对齐 */
font-size: 30px; /* 文字大小 */
line-height: 50px; /* 行高 */
}
.dialog-footer {
margin-top: 20px; /* 上外边距 */
text-align: right; /* 右对齐 */
}
</style>

@ -1,26 +1,45 @@
<template>
<!-- 整个页面的面板容器 -->
<div class="panel">
<!-- 根据条件控制显示内容当addOrUpdateVisible为false时显示以下内容 -->
<div v-show="!addOrUpdateVisible">
<!-- 内联表单绑定了dataForm数据模型 -->
<el-form :inline="true" :model="dataForm">
<!-- 根据selectMode的值决定是否显示该表单元素当不是选择模式时显示 -->
<el-form-item v-show="!selectMode">
<!-- 按钮根据权限判断是否显示调用isAuth方法判断类型为主要按钮点击时调用addOrUpdateHandle方法按钮尺寸为迷你型 -->
<el-button size="mini" v-if="isAuth('wx:wxassets:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
</el-form-item>
</el-form>
<!-- 加载提示容器根据dataListLoading的值显示加载状态 -->
<div class="flex justify-start" v-loading="dataListLoading">
<!-- 循环生成行n表示行号这里的rows应该是控制每行显示的元素数量相关 -->
<div v-for="n in rows" :key="n">
<!-- 循环遍历dataList数据列表用于展示具体的数据项 -->
<template v-for="(item,i) in dataList">
<!-- 根据条件显示卡片元素当满足i%rows==n-1时显示点击卡片会调用onSelect方法传递当前项数据 -->
<div class="card" :key="item.mediaId" v-if="i%rows==n-1" @click="onSelect(item)">
<!-- 卡片的预览部分用于展示文章相关信息 -->
<div class="card-preview">
<!-- 循环遍历文章数组展示每篇文章的信息点击文章链接会在新标签页打开target="_blank" -->
<a v-for="(article,k) in item.content.articles" :key="k" :href="article.url" class="article-item" target="_blank">
<!-- 文章标题做了多行文本截断显示处理 -->
<div class="article-title">{{article.title}}</div>
<!-- 文章缩略图通过绑定src属性显示图片 -->
<el-image class="article-thumb" :src="article.thumbUrl"></el-image>
</a>
</div>
<!-- 卡片的底部部分用于展示更新时间以及操作按钮等 -->
<div class="card-footer">
<!-- 显示更新时间使用了moment.js库通过$moment方法调用将时间格式化为日历格式 -->
<div>{{$moment(item.updateTime).calendar()}}</div>
<!-- 根据是否是选择模式决定是否显示以下操作按钮 -->
<div class="flex justify-between align-center" v-show="!selectMode">
<!-- 复制按钮点击复制media_id复制成功和失败分别调用对应的方法 -->
<el-button size="mini" type="text" icon="el-icon-copy-document" v-clipboard:copy="item.mediaId" v-clipboard:success="onCopySuccess" v-clipboard:error="onCopyError">复制media_id</el-button>
<!-- 编辑按钮点击调用addOrUpdateHandle方法传入当前项数据按钮类型为文本样式 -->
<el-button size="mini" type="text" icon="el-icon-edit" @click="addOrUpdateHandle(item)"></el-button>
<!-- 删除按钮点击调用deleteHandle方法传入mediaId进行删除操作按钮类型为文本样式 -->
<el-button size="mini" type="text" icon="el-icon-delete" @click="deleteHandle(item.mediaId)"></el-button>
</div>
</div>
@ -28,20 +47,23 @@
</template>
</div>
</div>
<!-- 分页组件绑定了相关的分页事件和属性用于切换页面获取不同页的数据 -->
<el-pagination @current-change="currentChangeHandle" :current-page="pageIndex" :page-size="pageSize" :total="totalCount" layout="total, prev,pager, next, jumper">
</el-pagination>
</div>
<!-- 新增 / 修改 -->
<!-- 新增/修改组件通过属性和事件与父组件进行交互 -->
<add-or-update :visible="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="onChange" @hide="addOrUpdateVisible=false"></add-or-update>
</div>
</template>
<script>
// /
import AddOrUpdate from './material-news-add-or-update'
export default {
name: 'material-news',
components: {
AddOrUpdate
},
//
props: {
selectMode: {//
type: Boolean,
@ -64,50 +86,63 @@ export default {
}
},
mounted(){
// init
this.init();
},
methods: {
init() {
// getDataList
if (!this.dataList.length) {
this.getDataList()
}
},
getDataList() {
//
if (this.dataListLoading) return
// true
this.dataListLoading = true
// HTTPURL
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialNewsBatchGet'),
params: this.$http.adornParams({
'page': this.pageIndex
})
}).then(({ data }) => {
//
if (data.code == 200) {
//
this.dataList = data.data.items
this.totalCount = data.data.totalCount
} else {
//
this.$message.error(data.msg);
}
// false
this.dataListLoading = false
})
},
onSelect(itemInfo) {
// $emitselected
if (!this.selectMode) return
this.$emit('selected', itemInfo)
},
//
//
deleteHandle(id) {
//
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'),
method: 'post',
data: { mediaId: id }
}).then(({ data }) => {
//
if (data && data.code === 200) {
// onChange
this.$message({
message: '操作成功',
type: 'success',
@ -115,17 +150,18 @@ export default {
onClose: () => this.onChange()
})
} else {
//
this.$message.error(data.msg)
}
})
})
},
//
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
// /
// //trueDOM/
addOrUpdateHandle(news) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
@ -133,12 +169,15 @@ export default {
})
},
onCopySuccess() {
//
this.$message.success('已复制')
},
onCopyError(err) {
//
this.$message.error('复制失败,可能是此浏览器不支持复制')
},
onChange() {
// 1$emitchange
this.pageIndex=1
this.getDataList()
this.$emit('change')

@ -1,14 +1,22 @@
<template>
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible" >
<!-- el-dialog组件用于弹出对话框根据dataForm中是否有id来设置标题为新增修改点击模态框背景不关闭对话框通过visible属性双向绑定控制显示隐藏 -->
<el-dialog :title="!dataForm.id? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible" >
<!-- el-form组件定义表单绑定dataForm数据模型应用dataRule验证规则设置标签宽度为80px -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="80px">
<!-- 规则名称的表单项对应dataForm中的ruleName属性设置了必填验证 -->
<el-form-item label="规则名称" prop="ruleName">
<!-- el-input组件实现输入框双向绑定dataForm.ruleName有占位提示文字 -->
<el-input v-model="dataForm.ruleName" placeholder="规则名称"></el-input>
</el-form-item>
<!-- 匹配词的表单项使用自定义的tags-editor组件双向绑定dataForm.matchValue用于输入或编辑匹配词可能是多个类似标签形式 -->
<el-form-item label="匹配词" prop="matchValue">
<tags-editor v-model="dataForm.matchValue"></tags-editor>
</el-form-item>
<!-- 第一行布局使用el-row包裹 -->
<el-row>
<!-- 占12列用于放置作用范围相关的表单元素 -->
<el-col :span="12">
<!-- 作用范围的表单项对应dataForm中的appid属性通过下拉选择框选择有全部公众号和当前公众号两个选项 -->
<el-form-item label="作用范围" prop="appid">
<el-select v-model="dataForm.appid" placeholder="作用范围">
<el-option label="全部公众号" value=""></el-option>
@ -16,71 +24,91 @@
</el-select>
</el-form-item>
</el-col>
<!-- 同样占12列用于放置精确匹配相关的表单元素使用el-switch组件实现开关切换功能双向绑定dataForm.exactMatch -->
<el-col :span="12">
<el-form-item label="精确匹配" prop="exactMatch">
<el-switch v-model="dataForm.exactMatch" :active-value="true" :inactive-value="false"></el-switch>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行布局 -->
<el-row>
<!-- 占12列用于放置回复类型相关的表单元素 -->
<el-col :span="12">
<!-- 回复类型的表单项对应dataForm中的replyType属性通过下拉选择框选择选项通过循环KefuMsgType生成选择改变时触发onReplyTypeChange方法 -->
<el-form-item label="回复类型" prop="replyType">
<el-select v-model="dataForm.replyType" @change="onReplyTypeChange">
<el-option v-for="(name,key) in KefuMsgType" :key="key" :value="key" :label="name"></el-option>
</el-select>
</el-form-item>
</el-col>
<!-- 占12列用于放置是否启用相关的表单元素使用el-switch组件实现开关切换功能双向绑定dataForm.status -->
<el-col :span="12">
<el-form-item label="是否启用" prop="status">
<el-switch v-model="dataForm.status" :active-value="true" :inactive-value="false"></el-switch>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行布局 -->
<el-row>
<!-- 占12列用于放置生效时间相关的表单元素使用el-time-picker组件实现时间选择功能绑定dataForm.effectTimeStart设置时间格式 -->
<el-col :span="12">
<el-form-item label="生效时间" prop="effectTimeStart">
<el-time-picker v-model="dataForm.effectTimeStart" value-format="HH:mm:ss"></el-time-picker>
</el-form-item>
</el-col>
<!-- 占12列用于放置失效时间相关的表单元素使用el-time-picker组件实现时间选择功能绑定dataForm.effectTimeEnd设置时间格式 -->
<el-col :span="12">
<el-form-item label="失效时间" prop="effectTimeEnd">
<el-time-picker v-model="dataForm.effectTimeEnd" value-format="HH:mm:ss"></el-time-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 回复内容的表单项对应dataForm中的replyContent属性使用el-input组件实现文本域输入框双向绑定dataForm.replyContent有行数设置和占位提示文字 -->
<el-form-item label="回复内容" prop="replyContent">
<el-input v-model="dataForm.replyContent" type="textarea" :rows="5" placeholder="文本、图文ID、media_id、json配置"></el-input>
<!-- 当回复类型为文本时显示插入链接按钮点击调用addLink方法在回复内容中添加链接 -->
<el-button type="text" v-show="'text'==dataForm.replyType" @click="addLink"></el-button>
<!-- 根据assetsType的值决定是否显示按钮点击打开素材选择器通过控制assetsSelectorVisible属性按钮文字根据回复类型有所不同 -->
<el-button type="text" v-show="assetsType" @click="assetsSelectorVisible=true">
从素材库中选择<span v-if="'miniprogrampage'==dataForm.replyType || 'music'==dataForm.replyType"></span>
</el-button>
</el-form-item>
<!-- 备注说明的表单项对应dataForm中的desc属性使用el-input组件实现输入框双向绑定dataForm.desc有占位提示文字 -->
<el-form-item label="备注说明" prop="desc">
<el-input v-model="dataForm.desc" placeholder="备注说明"></el-input>
</el-form-item>
</el-form>
<!-- 对话框底部的按钮区域通过插槽定义 -->
<span slot="footer" class="dialog-footer">
<!-- 取消按钮点击时设置visible为false关闭对话框 -->
<el-button @click="visible = false">取消</el-button>
<!-- 确定按钮类型为主要按钮点击时调用dataFormSubmit方法进行表单提交 -->
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
<!-- 素材选择器组件根据条件控制显示通过属性和事件与当前组件进行交互用于选择素材相关操作 -->
<assets-selector v-if="assetsSelectorVisible && assetsType" :visible="assetsSelectorVisible" :selectType="assetsType" @selected="onAssetsSelect" @onClose="assetsSelectorVisible=false"></assets-selector>
</el-dialog>
</template>
<script>
// VuexmapStateVuex
import { mapState } from 'vuex'
export default {
components: {
// tags-editor
tagsEditor: () => import('@/components/tags-editor'),
AssetsSelector:()=>import('./assets/assets-selector')
// AssetsSelector
AssetsSelector: () => import('./assets/assets-selector')
},
data() {
return {
visible: false,
assetsSelectorVisible:false,
assetsSelectorVisible: false,
dataForm: {
// ID0
ruleId: 0,
appid:'',
appid: '',
ruleName: "",
exactMatch: false,
matchValue: "",
@ -92,24 +120,31 @@ export default {
effectTimeEnd: "23:59:59"
},
dataRule: {
// blur
ruleName: [
{ required: true, message: "规则名称不能为空", trigger: "blur" }
],
//
matchValue: [
{ required: true, message: "匹配的关键词、事件等不能为空", trigger: "blur" }
],
//
replyType: [
{ required: true, message: "回复类型1:文本2:图文3媒体不能为空", trigger: "blur" }
],
//
replyContent: [
{ required: true, message: "回复内容不能为空", trigger: "blur" }
],
//
status: [
{ required: true, message: "是否有效不能为空", trigger: "blur" }
],
//
effectTimeStart: [
{ required: true, message: "生效起始时间不能为空", trigger: "blur" }
],
//
effectTimeEnd: [
{ required: true, message: "生效结束时间不能为空", trigger: "blur" }
]
@ -117,49 +152,60 @@ export default {
};
},
computed: mapState({
KefuMsgType: state=>state.message.KefuMsgType,
selectedAppid:state=>state.wxAccount.selectedAppid,
assetsType(){
const config={//
'image':'image',
'voice':'voice',
'video':'video',
'mpnews':'news',
'miniprogrampage':'image',//
'music':'image'
}
return config[this.dataForm.replyType] || ''
// VuexstatemessageKefuMsgType
KefuMsgType: state => state.message.KefuMsgType,
// VuexstatewxAccountselectedAppid
selectedAppid: state => state.wxAccount.selectedAppid,
assetsType() {
const config = {
//
'image': 'image',
'voice': 'voice',
'video': 'video',
'mpnews': 'news',
'miniprogrampage': 'image', //
'music': 'image'
};
return config[this.dataForm.replyType] || '';
}
}),
methods: {
init(id) {
// dataFormruleIdid0
this.dataForm.ruleId = id || 0;
// true
this.visible = true;
this.$nextTick(() => {
// DOM
this.$refs["dataForm"].resetFields();
if (this.dataForm.ruleId) {
// ruleIdHTTP
this.$http({
url: this.$http.adornUrl( `/manage/msgReplyRule/info/${this.dataForm.ruleId}` ),
url: this.$http.adornUrl(`/manage/msgReplyRule/info/${this.dataForm.ruleId}`),
method: "get",
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
// dataForm
this.dataForm = data.msgReplyRule;
}
});
}
});
},
//
//
dataFormSubmit() {
// valid
this.$refs["dataForm"].validate(valid => {
if (valid) {
// HTTPruleId
this.$http({
url: this.$http.adornUrl(`/manage/msgReplyRule/${!this.dataForm.ruleId ? "save" : "update"}`),
url: this.$http.adornUrl(`/manage/msgReplyRule/${!this.dataForm.ruleId? "save" : "update"}`),
method: "post",
data: this.$http.adornData(this.dataForm)
}).then(({ data }) => {
if (data && data.code === 200) {
// $emitrefreshDataList
this.$message({
message: "操作成功",
type: "success",
@ -170,6 +216,7 @@ export default {
}
});
} else {
//
this.$message.error(data.msg);
}
});
@ -177,9 +224,11 @@ export default {
});
},
addLink() {
//
this.dataForm.replyContent += '<a href="链接地址">链接文字</a>'
},
onReplyTypeChange(value) {
// JSON便使
if ("miniprogrampage" == value) {
let demo = { title: "标题", appid: "小程序APPID", pagepath: "页面地址", thumb_media_id: "缩略图media_id" };
this.dataForm.replyContent = JSON.stringify(demo, null, 4)
@ -190,21 +239,22 @@ export default {
let demo = { head_content: "开头文字", list: [{ id: "菜单1ID", content: "菜单2内容" }, { id: "菜单2ID", content: "菜单2内容" }, { id: "菜单nID", content: "菜单n内容" }], tail_content: "结尾文字" }
this.dataForm.replyContent = JSON.stringify(demo, null, 4)
} else if ("news" == value) {
let demo={title:"文章标题",description:"文章简介",url:"链接URL",picUrl:"缩略图URL"}
let demo = { title: "文章标题", description: "文章简介", url: "链接URL", picUrl: "缩略图URL" }
this.dataForm.replyContent = JSON.stringify(demo, null, 4)
} else {
this.dataForm.replyContent = '媒体素材media_id'
}
},
onAssetsSelect(assetsInfo){
if(this.dataForm.replyType=='miniprogrampage' || this.dataForm.replyType=='music'){
let data = JSON.parse(this.dataForm.replyContent)
if(data && data.thumb_media_id)data.thumb_media_id=assetsInfo.mediaId
this.dataForm.replyContent = JSON.stringify(data, null, 4)
}else{
this.dataForm.replyContent = assetsInfo.mediaId
onAssetsSelect(assetsInfo) {
// media_idmediaId
if (this.dataForm.replyType =='miniprogrampage' || this.dataForm.replyType =='music') {
let data = JSON.parse(this.dataForm.replyContent);
if (data && data.thumb_media_id) data.thumb_media_id = assetsInfo.mediaId;
this.dataForm.replyContent = JSON.stringify(data, null, 4);
} else {
this.dataForm.replyContent = assetsInfo.mediaId;
}
this.assetsSelectorVisible=false
this.assetsSelectorVisible = false;
}
}
};

@ -1,50 +1,72 @@
<template>
<!-- 整个模块的容器类用于包裹后续的表单表格分页等组件 -->
<div class="mod-config">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 匹配关键词输入框所在的表单项 -->
<el-form-item>
<!-- 使用el-input组件实现输入框双向绑定dataForm.matchValue有占位提示文字且可清空输入内容 -->
<el-input v-model="dataForm.matchValue" placeholder="匹配关键词" clearable></el-input>
</el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item>
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 -->
<el-button @click="getDataList()"></el-button>
<!-- 新增按钮根据权限调用isAuth方法判断决定是否显示类型为主要按钮点击时调用addOrUpdateHandle方法 -->
<el-button v-if="isAuth('wx:msgreplyrule:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:msgreplyrule:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框可展开行type="expand"加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border type="expand" v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 展开列通过插槽定义展开内容这里展示了更多规则相关详细信息的表单 -->
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" inline class="demo-table-expand">
<!-- 作用范围信息展示的表单项根据数据项中的appid值判断显示当前公众号全部公众号 -->
<el-form-item label="作用范围">
<span>{{ props.row.appid?'当前公众号':'全部公众号' }}</span>
<span>{{ props.row.appid? '当前公众号' : '全部公众号' }}</span>
</el-form-item>
<!-- 精确匹配信息展示的表单项根据数据项中的exactMatch值判断显示 -->
<el-form-item label="精确匹配">
<span>{{ props.row.exactMatch?'是':'否' }}</span>
<span>{{ props.row.exactMatch? '是' : '否' }}</span>
</el-form-item>
<!-- 是否有效信息展示的表单项根据数据项中的status值判断显示 -->
<el-form-item label="是否有效">
<span>{{ props.row.status?'是':'否' }}</span>
<span>{{ props.row.status? '是' : '否' }}</span>
</el-form-item>
<!-- 备注说明信息展示的表单项直接显示数据项中的desc值 -->
<el-form-item label="备注说明">
<span>{{ props.row.desc }}</span>
</el-form-item>
<!-- 生效时间信息展示的表单项直接显示数据项中的effectTimeStart值 -->
<el-form-item label="生效时间">
<span>{{ props.row.effectTimeStart }}</span>
</el-form-item>
<!-- 失效时间信息展示的表单项直接显示数据项中的effectTimeEnd值 -->
<el-form-item label="失效时间">
<span>{{ props.row.effectTimeEnd }}</span>
</el-form-item>
</el-form>
</template>
</el-table-column>
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<!-- 规则名称列对应dataList中数据项的ruleName属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="ruleName" header-align="center" align="center" show-overflow-tooltip label="规则名称">
</el-table-column>
<!-- 匹配关键词列对应dataList中数据项的matchValue属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="matchValue" header-align="center" align="center" show-overflow-tooltip label="匹配关键词">
</el-table-column>
<!-- 消息类型列对应dataList中数据项的replyType属性使用formatter函数格式化显示内容设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="replyType" header-align="center" align="center" :formatter="replyTypeFormat" label="消息类型">
</el-table-column>
<!-- 回复内容列对应dataList中数据项的replyContent属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="replyContent" header-align="center" align="center" show-overflow-tooltip label="回复内容">
</el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.ruleId)"></el-button>
@ -52,15 +74,18 @@
</template>
</el-table-column>
</el-table>
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<!-- 新增/修改组件通过属性和事件与父组件进行交互根据addOrUpdateVisible的值决定是否显示 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script>
// /
import AddOrUpdate from './msg-reply-rule-add-or-update'
// VuexmapStateVuex
import { mapState } from 'vuex'
export default {
components: {
@ -81,16 +106,20 @@ export default {
}
},
computed: mapState({
KefuMsgType: state=>state.message.KefuMsgType
// VuexstatemessageKefuMsgType
KefuMsgType: state => state.message.KefuMsgType
}),
activated() {
// getDataList
this.getDataList()
},
methods: {
//
//
getDataList() {
// true
this.dataListLoading = true
// HTTPURL
this.$http({
url: this.$http.adornUrl('/manage/msgReplyRule/list'),
method: 'get',
@ -100,52 +129,60 @@ export default {
'matchValue': this.dataForm.matchValue
})
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.dataList = data.page.list
this.totalCount = data.page.totalCount
} else {
//
this.dataList = []
this.totalCount = 0
}
// false
this.dataListLoading = false
})
},
//
// 1
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
//
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
//
selectionChangeHandle(val) {
this.dataListSelections = val
},
// /
// //trueDOM/id
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
//
// idHTTP
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.ruleId)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
// id使ruleId
var ids = id? [id] : this.dataListSelections.map(item => item.ruleId)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/msgReplyRule/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
@ -153,14 +190,16 @@ export default {
onClose: () => this.getDataList()
})
} else {
//
this.$message.error(data.msg)
}
})
})
},
// KefuMsgType
replyTypeFormat(row, column, cellValue) {
return this.KefuMsgType[cellValue];
}
}
}
</script>
</script>

@ -1,165 +1,195 @@
<template>
<el-dialog title="模板配置" :close-on-click-modal="false" :visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="100px" size="mini">
<el-form-item label="标题" prop="title">
<el-input v-model="dataForm.title" placeholder="标题"></el-input>
</el-form-item>
<el-form-item label="链接" prop="url">
<el-input v-model="dataForm.url" placeholder="跳转链接"></el-input>
</el-form-item>
<div>
<el-form-item label="小程序appid" prop="miniprogram.appid">
<el-input v-model="dataForm.miniprogram.appid" placeholder="小程序appid"></el-input>
</el-form-item>
<el-form-item label="小程序路径" prop="miniprogram.pagePath">
<el-input v-model="dataForm.miniprogram.pagePath" placeholder="小程序pagePath"></el-input>
</el-form-item>
</div>
<el-row>
<el-col :span="16">
<el-form-item label="模版名称" prop="name">
<el-input v-model="dataForm.name" placeholder="模版名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="有效" prop="status">
<el-switch v-model="dataForm.status" placeholder="是否有效" :active-value="true" :inactive-value="false"></el-switch>
</el-form-item>
</el-col>
</el-row>
<div class="form-group-area">
<el-form-item class="form-group-title">消息填充数据请对照模板内容填写</el-form-item>
<el-form-item>
<el-input type="textarea" disabled autosize v-model="dataForm.content" placeholder="模版"></el-input>
</el-form-item>
<el-row v-for="(item,index) in dataForm.data" :key="item.name">
<el-col :span="16">
<el-form-item :label="item.name" :prop="'data.'+index+'.value'" :rules="[{required: true,message: '填充内容不得为空', trigger: 'blur' }]">
<el-input type="textarea" autosize rows="1" v-model="item.value" placeholder="填充内容" ></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="颜色" >
<el-input type="color" v-model="item.color" placeholder="颜色"></el-input>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<template>
<!-- el-dialog组件用于创建一个对话框设置了标题点击模态框不关闭双向绑定显示隐藏状态等属性 -->
<el-dialog title="模板配置" :close-on-click-modal="false" :visible.sync="visible">
<!-- el-form组件用于创建表单绑定了表单数据模型验证规则设置了标签宽度尺寸等属性 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="100px" size="mini">
<!-- 表单项目用于输入标题绑定了dataForm中的title字段设置了提示文字 -->
<el-form-item label="标题" prop="title">
<el-input v-model="dataForm.title" placeholder="标题"></el-input>
</el-form-item>
<!-- 表单项目用于输入链接绑定了dataForm中的url字段设置了提示文字 -->
<el-form-item label="链接" prop="url">
<el-input v-model="dataForm.url" placeholder="跳转链接"></el-input>
</el-form-item>
<div>
<!-- 表单项目用于输入小程序appid绑定了dataForm.miniprogram.appid字段设置了提示文字 -->
<el-form-item label="小程序appid" prop="miniprogram.appid">
<el-input v-model="dataForm.miniprogram.appid" placeholder="小程序appid"></el-input>
</el-form-item>
<!-- 表单项目用于输入小程序路径绑定了dataForm.miniprogram.pagePath字段设置了提示文字 -->
<el-form-item label="小程序路径" prop="miniprogram.pagePath">
<el-input v-model="dataForm.miniprogram.pagePath" placeholder="小程序pagePath"></el-input>
</el-form-item>
</div>
<el-row>
<el-col :span="16">
<!-- 表单项目用于输入模版名称绑定了dataForm中的name字段设置了提示文字 -->
<el-form-item label="模版名称" prop="name">
<el-input v-model="dataForm.name" placeholder="模版名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- 表单项目使用el-switch组件用于切换是否有效状态绑定了dataForm中的status字段 -->
<el-form-item label="有效" prop="status">
<el-switch v-model="dataForm.status" placeholder="是否有效" :active-value="true" :inactive-value="false"></el-switch>
</el-form-item>
</el-col>
</el-row>
<div class="form-group-area">
<!-- 表单项目用于显示提示信息告知消息填充数据的相关要求 -->
<el-form-item class="form-group-title">消息填充数据请对照模板内容填写</el-form-item>
<!-- 表单项目使用textarea类型的el-input展示模板内容设置为禁用状态 -->
<el-form-item>
<el-input type="textarea" disabled autosize v-model="dataForm.content" placeholder="模版"></el-input>
</el-form-item>
<!-- 使用v-for循环遍历dataForm.data数组动态生成表单项目用于输入消息填充数据及设置颜色 -->
<el-row v-for="(item, index) in dataForm.data" :key="item.name">
<el-col :span="16">
<!-- 表单项目根据循环的索引和字段名绑定对应的数据项的value字段设置了必填验证规则 -->
<el-form-item :label="item.name" :prop="'data.' + index + '.value'" :rules="[{required: true, message: '填充内容不得为空', trigger: 'blur' }]">
<el-input type="textarea" autosize rows="1" v-model="item.value" placeholder="填充内容"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- 表单项目用于输入颜色值绑定对应数据项的color字段 -->
<el-form-item label="颜色">
<el-input type="color" v-model="item.color" placeholder="颜色"></el-input>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
<!-- 在对话框的底部插槽中添加取消和确定按钮分别绑定对应的点击事件 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data() {
return {
visible: false,
dataForm: {
id: 0,
templateId: '',
title: '',
data: [],
url: '',
miniprogram:{appid:'',pagePath:''},
content: '',
status: true,
name: ''
},
dataRule: {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
data: [
{ required: true, message: '内容不能为空', trigger: 'blur' }
],
name: [
{ required: true, message: '模版名称不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init(id) {
console.log('init',id)
this.dataForm.id = id || 0
this.visible = true
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
if (this.dataForm.id) {
this.$http({
url: this.$http.adornUrl(`/manage/msgTemplate/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
this.transformTemplate(data.msgTemplate)
}else{
this.$message.error(data.msg)
}
})
}
})
},
/**
* 根据content信息展开data配置项(content为微信公众平台后台配置的模板)
* 如content='{{first.DATA}} ↵商品名称:{{keyword1.DATA}} ↵购买时间:{{keyword2.DATA}} ↵{{remark.DATA}}'
* 则生成data=[{name:'first',value:'',color:''},{name:'first',value:'',color:''},{name:'first',value:'',color:''}]
* 展示表单让管理员给对应的字段填充内容
*/
transformTemplate(template){
if(!template.miniprogram)template.miniprogram={appid:'',pagePath:''}
if(template.data instanceof Array) {//
this.dataForm = template
return
}
<script>
export default {
data() {
return {
visible: false, //
// idtemplateId
dataForm: {
id: 0,
templateId: '',
title: '',
data: [], //
url: '',
miniprogram: { appid: '', pagePath: '' }, //
content: '', //
status: true, //
name: '' //
},
//
dataRule: {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
data: [
{ required: true, message: '内容不能为空', trigger: 'blur' }
],
name: [
{ required: true, message: '模版名称不能为空', trigger: 'blur' }
]
}
}
},
methods: {
// id
init(id) {
console.log('init', id);
// idid0
this.dataForm.id = id || 0;
//
this.visible = true;
this.$nextTick(() => {
//
this.$refs['dataForm'].resetFields();
if (this.dataForm.id) {
// id使axios$httpaxiosGET
this.$http({
url: this.$http.adornUrl(`/manage/msgTemplate/info/${this.dataForm.id}`),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.transformTemplate(data.msgTemplate);
} else {
//
this.$message.error(data.msg);
}
});
}
});
},
/**
* 根据content信息展开data配置项(content为微信公众平台后台配置的模板)
* 如content='{{first.DATA}} ↵商品名称:{{keyword1.DATA}} ↵购买时间:{{keyword2.DATA}} ↵{{remark.DATA}}'
* 则生成data=[{name:'first',value:'',color:''},{name:'first',value:'',color:''},{name:'first',value:'',color:''}]
* 展示表单让管理员给对应的字段填充内容
*/
transformTemplate(template) {
if (!template.miniprogram) template.miniprogram = { appid: '', pagePath: '' };
if (template.data instanceof Array) { //
this.dataForm = template;
return;
}
template.data=[]
let keysArray = template.content.match(/\{\{(\w*)\.DATA\}\}/g) || [] // ["{{first.DATA}}", "{{keyword1.DATA}}", "{{keyword2.DATA}}", "{{remark.DATA}}"]
keysArray.map(item=>{
name=item.replace('{{','').replace('.DATA}}','')
template.data.push({"name":name,"value":"",color:"#000000"})
})
this.dataForm = template
},
//
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/manage/msgTemplate/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData(this.dataForm)
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script>
<style scoped>
.form-group-area{
border:1px dotted gray;
}
.form-group-title{
color: gray;
font-size: 12px;
}
template.data = [];
// content{{xxx.DATA}}xxxkeysArray
let keysArray = template.content.match(/\{\{(\w*)\.DATA\}\}/g) || []; // ["{{first.DATA}}", "{{keyword1.DATA}}", "{{keyword2.DATA}}", "{{remark.DATA}}"]
keysArray.map(item => {
const name = item.replace('{{', '').replace('.DATA}}', '');
// datavalue
template.data.push({ "name": name, "value": "", color: "#000000" });
});
this.dataForm = template; // dataForm
},
//
dataFormSubmit() {
//
this.$refs['dataForm'].validate((valid) => {
if (valid) {
// 使axios$httpaxiosPOSTid
this.$http({
url: this.$http.adornUrl(`/manage/msgTemplate/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData(this.dataForm) //
}).then(({ data }) => {
if (data && data.code === 200) {
// refreshDataList
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false; //
this.$emit('refreshDataList'); //
}
});
} else {
//
this.$message.error(data.msg);
}
});
}
});
}
}
}
</script>
<style scoped>
.form-group-area {
border: 1px dotted gray; /* 添加边框样式,使其更明显 */
}
.form-group-title {
color: gray; /* 设置标题颜色 */
font-size: 12px; /* 设置标题字体大小 */
}
</style>

@ -1,36 +1,55 @@
<template>
<!-- 整个模块的容器类用于包裹后续的表单表格分页以及相关弹窗组件等 -->
<div class="mod-config">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 标题输入框所在的表单项使用el-input组件实现输入框双向绑定dataForm.title有占位提示文字且可清空输入内容 -->
<el-form-item>
<el-input v-model="dataForm.title" placeholder="标题" clearable></el-input>
</el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item>
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 -->
<el-button @click="getDataList()"></el-button>
<!-- 批量复制按钮根据权限调用isAuth方法判断决定是否显示类型为成功按钮点击时调用copyHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:msgtemplate:save')" type="success" @click="copyHandle()" :disabled="dataListSelections.length <= 0"></el-button>
<el-button v-if="isAuth('wx:msgtemplate:save')" type="success" @click="templateMsgTaskHandle()" :disabled="dataListSelections.length!=1"></el-button>
<!-- 推送消息按钮根据权限决定是否显示类型为成功按钮点击时调用templateMsgTaskHandle方法当选中的数据项数量不为1时禁用按钮 -->
<el-button v-if="isAuth('wx:msgtemplate:save')" type="success" @click="templateMsgTaskHandle()" :disabled="dataListSelections.length!= 1"></el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:msgtemplate:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
<!-- 靠右对齐的表单项包含同步公众号模板按钮和模板管理指引链接按钮 -->
<el-form-item class="fr">
<el-button v-if="isAuth('wx:msgtemplate:save')" icon="el-icon-sort" type="success" @click="syncWxTemplate()" :disabled="synchonizingWxTemplate">{{synchonizingWxTemplate?'...':''}}</el-button>
<!-- 同步公众号模板按钮根据权限决定是否显示点击时调用syncWxTemplate方法按钮文字根据同步状态动态变化正在同步时显示同步中...否则显示同步公众号模板同步过程中按钮禁用 -->
<el-button v-if="isAuth('wx:msgtemplate:save')" icon="el-icon-sort" type="success" @click="syncWxTemplate()" :disabled="synchonizingWxTemplate">{{synchonizingWxTemplate? '...' : ''}}</el-button>
<!-- 模板管理指引链接按钮点击后在新标签页打开指定的链接 -->
<el-button><el-link type="primary" icon="el-icon-link" target="_blank" href="https://kf.qq.com/faq/170209E3InyI170209nIF7RJ.html">模板管理指引</el-link></el-button>
</el-form-item>
</el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<!-- 模板ID列对应dataList中数据项的templateId属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="templateId" show-overflow-tooltip header-align="center" align="center" label="模板ID">
</el-table-column>
<!-- 标题列对应dataList中数据项的title属性通过插槽使用a标签包裹内容使其可点击跳转链接地址取自数据项的url属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="title" header-align="center" align="center" label="标题">
<a :href="scope.row.url" slot-scope="scope">{{scope.row.title}}</a>
</el-table-column>
<!-- 模版名称列对应dataList中数据项的name属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="name" header-align="center" align="center" label="模版名称">
</el-table-column>
<!-- 模版字段列对应dataList中数据项的content属性显示溢出提示设置了表头和内容的对齐方式以及列宽度 -->
<el-table-column prop="content" show-overflow-tooltip header-align="center" align="center" label="模版字段" width="200">
</el-table-column>
<!-- 是否有效列对应dataList中数据项的status属性通过插槽根据数据项的status值判断显示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="status" header-align="center" align="center" label="是否有效">
<span slot-scope="scope">{{scope.row.status?"是":"否"}}</span>
<span slot-scope="scope">{{scope.row.status? "是" : "否"}}</span>
</el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了配置和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button>
@ -38,16 +57,22 @@
</template>
</el-table-column>
</el-table>
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<!-- 新增/修改组件通过属性和事件与父组件进行交互根据addOrUpdateVisible的值决定是否显示 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
<!-- 模板消息任务组件根据templateMsgTaskVisible的值决定是否显示 -->
<template-msg-task v-if="templateMsgTaskVisible" ref="templateMsgTask"></template-msg-task>
</div>
</template>
<script>
// /
import AddOrUpdate from './msg-template-add-or-update'
//
import TemplateMsgTask from '@/components/template-msg-task'
export default {
data() {
@ -62,20 +87,23 @@ export default {
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false,
templateMsgTaskVisible:false,
synchonizingWxTemplate:false
templateMsgTaskVisible: false,
synchonizingWxTemplate: false
}
},
components: {
AddOrUpdate,TemplateMsgTask
AddOrUpdate, TemplateMsgTask
},
activated() {
// getDataList
this.getDataList()
},
methods: {
//
//
getDataList() {
this.dataListLoading = true
// true
this.dataListLoading = true;
// HTTPURL
this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/list'),
method: 'get',
@ -87,128 +115,159 @@ export default {
'order': 'desc'
})
}).then(({ data }) => {
//
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount = data.page.totalCount
//
this.dataList = data.page.list;
this.totalCount = data.page.totalCount;
} else {
this.dataList = []
this.totalCount = 0
//
this.dataList = [];
this.totalCount = 0;
}
this.dataListLoading = false
})
// false
this.dataListLoading = false;
});
},
//
// 1
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
this.pageSize = val;
this.pageIndex = 1;
this.getDataList();
},
//
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
this.pageIndex = val;
this.getDataList();
},
//
//
selectionChangeHandle(val) {
this.dataListSelections = val
this.dataListSelections = val;
},
// /
// //trueDOM/id
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
this.$refs.addOrUpdate.init(id);
});
},
//
// idHTTP
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.id)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
// id使id
var ids = id? [id] : this.dataListSelections.map(item => item.id);
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
this.getDataList();
}
})
});
} else {
this.$message.error(data.msg)
//
this.$message.error(data.msg);
}
})
})
});
});
},
syncWxTemplate(){
if(this.synchonizingWxTemplate)return
this.synchonizingWxTemplate=true
syncWxTemplate() {
//
if (this.synchonizingWxTemplate) return;
// true
this.synchonizingWxTemplate = true;
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/syncWxTemplate'),
method: 'post',
}).then(({ data }) => {
this.synchonizingWxTemplate=false
if (data && data.code === 200) {
this.$message({
message: '同步完成',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
}).catch(()=>this.synchonizingWxTemplate=false)
url: this.$http.adornUrl('/manage/msgTemplate/syncWxTemplate'),
method: 'post'
}).then(({ data }) => {
// false
this.synchonizingWxTemplate = false;
//
if (data && data.code === 200) {
//
this.$message({
message: '同步完成',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList();
}
});
} else {
//
this.$message.error(data.msg);
}
}).catch(() => this.synchonizingWxTemplate = false);
},
templateMsgTaskHandle(){
this.templateMsgTaskVisible = true
templateMsgTaskHandle() {
// true
this.templateMsgTaskVisible = true;
this.$nextTick(() => {
this.$refs.templateMsgTask.init(this.dataListSelections[0])
})
// DOM
this.$refs.templateMsgTask.init(this.dataListSelections[0]);
});
},
async copyHandle(){
async copyHandle() {
let loading;
//
for (let i = 0; i < this.dataListSelections.length; i++) {
let item = this.dataListSelections[i];
loading=this.$loading({
//
loading = this.$loading({
lock: true,
text: "复制模板:"+item.title,
text: "复制模板:" + item.title,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
item.id='';
item.updateTime=new Date()
item.name+='_COPY'
await this.addMsgTemplate(item).catch(()=>loading.close())
loading.close()
// id
item.id = '';
//
item.updateTime = new Date();
// _COPY
item.name += '_COPY';
// addMsgTemplate使await
await this.addMsgTemplate(item).catch(() => loading.close());
//
loading.close();
}
loading.close()
this.getDataList()
//
loading.close();
//
this.getDataList();
},
addMsgTemplate(msgTemplate){
addMsgTemplate(msgTemplate) {
return new Promise((resolve, reject) => {
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/save'),
method: 'post',
data: this.$http.adornData(msgTemplate)
}).then(({ data }) => {
//
if (data && data.code === 200) {
resolve()
// resolve
resolve();
} else {
this.$message.error(data.msg)
reject(data.msg)
// reject
this.$message.error(data.msg);
reject(data.msg);
}
}).catch(err=>reject(err))
})
}).catch(err => reject(err));
});
}
}
}

@ -1,38 +1,57 @@
<template>
<!-- 整个模块的容器类用于包裹后续的表单表格以及分页组件等 -->
<div class="mod-config">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- openid输入框所在的表单项使用el-input组件实现输入框双向绑定dataForm.touser有占位提示文字且可清空输入内容 -->
<el-form-item>
<el-input v-model="dataForm.touser" placeholder="openid" clearable></el-input>
</el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item>
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 -->
<el-button @click="getDataList()"></el-button>
<!-- 批量删除按钮根据权限调用isAuth方法判断决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:templatemsglog:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<!-- openid列对应dataList中数据项的touser属性设置了表头和内容的对齐方式以及列宽度 -->
<el-table-column prop="touser" header-align="center" align="center" label="openid" width="100">
</el-table-column>
<!-- 内容列对应dataList中数据项的data属性使用formatter函数tableJsonFormat方法格式化显示内容设置了表头和内容的对齐方式以及列宽度 -->
<el-table-column prop="data" header-align="center" align="center" :formatter="tableJsonFormat" label="内容" width="300">
</el-table-column>
<!-- 发送结果列对应dataList中数据项的sendResult属性显示溢出提示设置了表头和内容的对齐方式以及列标题和宽度 -->
<el-table-column prop="sendResult" header-align="center" align="center" show-overflow-tooltip label="发送结果" width="150">
</el-table-column>
<!-- 发送时间列对应dataList中数据项的sendTime属性设置了表头和内容的对齐方式以及列标题和宽度 -->
<el-table-column prop="sendTime" header-align="center" align="center" width="100" label="发送时间">
</el-table-column>
<!-- 链接列对应dataList中数据项的url属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="url" header-align="center" align="center" show-overflow-tooltip label="链接">
</el-table-column>
<!-- 小程序列对应dataList中数据项的miniprogram属性使用formatter函数tableJsonFormat方法格式化显示内容显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="miniprogram" header-align="center" align="center" :formatter="tableJsonFormat" show-overflow-tooltip label="小程序">
</el-table-column>
<!-- 模板ID列对应dataList中数据项的templateId属性设置了表头和内容的对齐方式以及列标题和宽度 -->
<el-table-column prop="templateId" header-align="center" align="center" label="模板ID" width="150">
</el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteHandle(scope.row.logId)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="current-changeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</div>
</template>
@ -42,6 +61,7 @@ export default {
data() {
return {
dataForm: {
// openid
touser: ''
},
dataList: [],
@ -54,12 +74,15 @@ export default {
}
},
activated() {
// getDataList
this.getDataList()
},
methods: {
//
//
getDataList() {
this.dataListLoading = true
// true
this.dataListLoading = true;
// HTTPURL
this.$http({
url: this.$http.adornUrl('/manage/templateMsgLog/list'),
method: 'get',
@ -71,65 +94,76 @@ export default {
'order': 'desc'
})
}).then(({ data }) => {
//
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount = data.page.totalCount
//
this.dataList = data.page.list;
this.totalCount = data.page.totalCount;
} else {
this.dataList = []
this.totalCount = 0
//
this.dataList = [];
this.totalCount = 0;
}
this.dataListLoading = false
})
// false
this.dataListLoading = false;
});
},
//
// 1
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
this.pageSize = val;
this.pageIndex = 1;
this.getDataList();
},
//
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
this.pageIndex = val;
this.getDataList();
},
//
//
selectionChangeHandle(val) {
this.dataListSelections = val
this.dataListSelections = val;
},
//
// idHTTP
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.logId)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
// id使logId
var ids = id? [id] : this.dataListSelections.map(item => item.logId);
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/templateMsgLog/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
this.getDataList();
}
})
});
} else {
this.$message.error(data.msg)
//
this.$message.error(data.msg);
}
})
})
});
});
},
tableJsonFormat(row, column, cellValue){
tableJsonFormat(row, column, cellValue) {
//
if (!cellValue) {
return '';
}
return JSON.stringify(cellValue)
// JSONJSON便
return JSON.stringify(cellValue);
}
}
}
</script>
</script>

@ -1,41 +1,42 @@
<template>
<!-- 整个模块的容器类用于包裹后续的表单表格以及相关弹窗组件等 -->
<div class="mod-config">
<!-- 定义一个内联表单用于搜索和操作 -->
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 参数名输入框所在的表单项使用el-input组件实现输入框双向绑定dataForm.key有占位提示文字且可清空输入内容 -->
<el-form-item>
<!-- 输入框用于输入搜索关键词 -->
<el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
</el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item>
<!-- 查询按钮触发数据列表的获取 -->
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 -->
<el-button @click="getDataList()"></el-button>
<!-- 新增按钮根据权限显示 -->
<!-- 新增按钮根据权限调用isAuth方法判断决定是否显示类型为主要按钮点击时调用addOrUpdateHandle方法 -->
<el-button v-if="isAuth('wx:wxaccount:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮根据权限和选中项显示 -->
<el-button v-if="isAuth('wx:wxaccount:delete')" type="danger" @click="deleteHandle()"
:disabled="dataListSelections.length <= 0">批量删除</el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:wxaccount:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item>
</el-form>
<!-- 数据列表展示 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle"
style="width: 100%;">
<!-- 选择-->
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column>
<!-- appid列 -->
<!-- appid列对应dataList中数据项的appid属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="appid" header-align="center" align="center" label="appid">
</el-table-column>
<!-- 公众号名称列 -->
<!-- 公众号名称列对应dataList中数据项的name属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="name" header-align="center" align="center" label="公众号名称">
</el-table-column>
<!-- 类型列使用formatter格式化显示 -->
<!-- 类型列对应dataList中数据项的type属性使用formatter函数accountTypeFormat方法格式化显示内容设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="type" header-align="center" align="center" label="类型" :formatter="accountTypeFormat">
</el-table-column>
<!-- 是否认证列使用插槽自定义显示 -->
<!-- 是否认证列对应dataList中数据项的verified属性通过插槽根据数据项的verified值判断显示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="verified" header-align="center" align="center" label="是否认证">
<span slot-scope="scope">{{ scope.row.verified ? "是" : "否" }}</span>
<span slot-scope="scope">{{scope.row.verified? "是" : "否"}}</span>
</el-table-column>
<!-- 操作列包含接入修改和删除按钮 -->
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了接入修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="accessInfo(scope.row)"></el-button>
@ -44,102 +45,129 @@
</template>
</el-table-column>
</el-table>
<!-- 新增/修改弹窗组件 -->
<!-- 新增/修改组件通过属性和事件与父组件进行交互根据addOrUpdateVisible的值决定是否显示 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
<!-- 接入信息弹窗组件 -->
<!-- 账号接入信息组件根据accountAccessVisible的值决定是否显示 -->
<account-access v-if="accountAccessVisible" ref="accountAccessDialog"></account-access>
</div>
</template>
<script>
import AddOrUpdate from './account/wx-account-add-or-update' // /
import AccountAccess from './account/wx-account-access-info' //
import { mapState } from 'vuex' // VuexmapState
// /
import AddOrUpdate from './account/wx-account-add-or-update'
//
import AccountAccess from './account/wx-account-access-info'
// VuexmapStateVuex
import { mapState } from 'vuex'
export default {
data() {
return {
dataForm: { key: '' }, //
dataList: [], //
dataListLoading: false, //
dataListSelections: [], //
addOrUpdateVisible: false, // /
accountAccessVisible: false //
dataForm: {
//
key: ''
},
dataList: [],
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false,
accountAccessVisible: false
}
},
components: { AddOrUpdate, AccountAccess }, //
components: {
AddOrUpdate, AccountAccess
},
computed: mapState({
ACCOUNT_TYPES: state => state.wxAccount.ACCOUNT_TYPES // Vuex
// VuexstatewxAccountACCOUNT_TYPES
ACCOUNT_TYPES: state => state.wxAccount.ACCOUNT_TYPES
}),
activated() {
this.getDataList() //
// getDataList
this.getDataList()
},
methods: {
//
//
getDataList() {
this.dataListLoading = true
// true
this.dataListLoading = true;
// HTTPURL
this.$http({
url: this.$http.adornUrl('/manage/wxAccount/list'), //
url: this.$http.adornUrl('/manage/wxAccount/list'),
method: 'get',
params: this.$http.adornParams({ 'key': this.dataForm.key }) //
params: this.$http.adornParams({
'key': this.dataForm.key
})
}).then(({ data }) => {
//
if (data && data.code === 200) {
this.dataList = data.list //
this.$store.commit('wxAccount/updateAccountList', data.list) // Vuex
// Vuexmutation
this.dataList = data.list;
this.$store.commit('wxAccount/updateAccountList', data.list);
} else {
this.dataList = [] //
//
this.dataList = [];
}
this.dataListLoading = false //
})
// false
this.dataListLoading = false;
});
},
//
//
selectionChangeHandle(val) {
this.dataListSelections = val //
this.dataListSelections = val;
},
// /
// //trueDOM/
addOrUpdateHandle(item) {
this.addOrUpdateVisible = true //
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(item) //
})
this.$refs.addOrUpdate.init(item);
});
},
//
accessInfo(item) {
this.accountAccessVisible = true //
// true
this.accountAccessVisible = true;
this.$nextTick(() => {
this.$refs.accountAccessDialog.init(item) //
})
// DOM
this.$refs.accountAccessDialog.init(item);
});
},
//
// appidHTTP
deleteHandle(appid) {
var ids = appid ? [appid] : this.dataListSelections.map(item => item.appid) // appid
this.$confirm(`确定对[appid=${ids.join(',')}]进行[${appid ? '删除' : '批量删除'}]操作?`, '提示', {
// appid使appid
var ids = appid? [appid] : this.dataListSelections.map(item => {
return item.appid
});
this.$confirm(`确定对[appid=${ids.join(',')}]进行[${appid? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// HTTP
this.$http({
url: this.$http.adornUrl('/manage/wxAccount/delete'), //
url: this.$http.adornUrl('/manage/wxAccount/delete'),
method: 'post',
data: this.$http.adornData(ids, false) //
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
//
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList() //
this.getDataList();
}
})
});
} else {
this.$message.error(data.msg) //
//
this.$message.error(data.msg);
}
})
})
});
});
},
//
accountTypeFormat(row, column, cellValue) {
return this.ACCOUNT_TYPES[cellValue] //
// ACCOUNT_TYPES
return this.ACCOUNT_TYPES[cellValue];
}
}
}

@ -1,21 +1,24 @@
<template>
<!-- 使用element-ui的el-tabs组件创建一个标签页切换组件 -->
<!-- 使用el-tabs组件创建选项卡通过v-model双向绑定activeTab来控制当前选中的选项卡监听tab-click事件触发handleTabClick方法 -->
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<!-- 图片素材标签页使用el-tab-pane组件定义每个标签页的内容 -->
<el-tab-pane :label="'图片素材(' + assetsCount.imageCount + ')'" name="image" lazy>
<!-- 引入自定义的MaterialFile组件用于展示图片素材 -->
<!-- 图片素材选项卡面板标签显示为图片素材数量数量通过assetsCount.imageCount动态获取设置name属性为image采用懒加载模式 -->
<el-tab-pane :label="'图片素材('+assetsCount.imageCount+')'" name="image" lazy>
<!-- 引入自定义组件material-file设置其fileType属性为image用于展示和管理图片素材相关功能同时监听其@change事件触发materialCount方法 -->
<material-file fileType="image" ref="imagePanel" @change="materialCount"></material-file>
</el-tab-pane>
<!-- 语音素材标签页 -->
<el-tab-pane :label="'语音素材(' + assetsCount.voiceCount + ')'" name="voice" lazy>
<!-- 语音素材选项卡面板标签显示为语音素材数量数量通过assetsCount.voiceCount动态获取设置name属性为voice采用懒加载模式 -->
<el-tab-pane :label="'语音素材('+assetsCount.voiceCount+')'" name="voice" lazy>
<!-- 引入自定义组件material-file设置其fileType属性为voice用于展示和管理语音素材相关功能同时监听其@change事件触发materialCount方法 -->
<material-file fileType="voice" ref="voicePanel" @change="materialCount"></material-file>
</el-tab-pane>
<!-- 视频素材标签页 -->
<el-tab-pane :label="'视频素材(' + assetsCount.videoCount + ')'" name="video" lazy>
<!-- 视频素材选项卡面板标签显示为视频素材数量数量通过assetsCount.videoCount动态获取设置name属性为video采用懒加载模式 -->
<el-tab-pane :label="'视频素材('+assetsCount.videoCount+')'" name="video" lazy>
<!-- 引入自定义组件material-file设置其fileType属性为video用于展示和管理视频素材相关功能同时监听其@change事件触发materialCount方法 -->
<material-file fileType="video" ref="videoPanel" @change="materialCount"></material-file>
</el-tab-pane>
<!-- 图文素材标签页使用自定义的MaterialNews组件 -->
<el-tab-pane :label="'图文素材(' + assetsCount.newsCount + ')'" name="news" lazy>
<!-- 图文素材选项卡面板标签显示为图文素材数量数量通过assetsCount.newsCount动态获取设置name属性为news采用懒加载模式 -->
<el-tab-pane :label="'图文素材('+assetsCount.newsCount+')'" name="news" lazy>
<!-- 引入自定义组件material-news用于展示和管理图文素材相关功能同时监听其@change事件触发materialCount方法 -->
<material-news ref="newsPanel" @change="materialCount"></material-news>
</el-tab-pane>
</el-tabs>
@ -25,49 +28,54 @@
export default {
data() {
return {
//
// el-tabsimage
activeTab: 'image',
//
assetsCount: {
imageCount: '..', //
videoCount: '..', //
voiceCount: '..', //
newsCount: '..' //
// ..
imageCount: '..',
// ..
videoCount: '..',
// ..
voiceCount: '..',
// ..
newsCount: '..'
}
};
},
// MaterialFileMaterialNews
components: {
// material-file
MaterialFile: () => import('./assets/material-file'),
// material-news
MaterialNews: () => import('./assets/material-news')
},
//
mounted() {
this.materialCount(); // materialCount
// materialCount
this.materialCount();
},
methods: {
//
handleTabClick(tab, event) {
// DOM
this.$nextTick(() => {
// DOMinit
this.$refs[tab.name + 'Panel'].init()
})
// refinitinit
this.$refs[tab.name + 'Panel'].init();
});
},
//
materialCount() {
// 使$httpaxios
// HTTPURL
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialCount') // URL
url: this.$http.adornUrl('/manage/wxAssets/materialCount')
}).then(({ data }) => {
//
if (data && data.code == 200) {
// assetsCount
this.assetsCount = data.data
// assetsCount
this.assetsCount = data.data;
} else {
//
//
this.$message.error(data.msg);
}
})
});
}
}
};
</script>

@ -1,38 +1,38 @@
<template>
<!-- 最外层的 div 作为整体容器 -->
<div>
<!-- 菜单项的输入底部边框 -->
<!-- 菜单输入组容器设置底部边框样式用于展示菜单名称以及删除菜单按钮 -->
<div class="menu-input-group" style="border-bottom: 2px #e8e8e8 solid;">
<!-- 显示菜单名称 -->
<div class="menu-name">{{ button.name }}</div>
<!-- 删除菜单项的按钮点击时触发'delMenu'事件 -->
<!-- 展示菜单名称通过插值表达式绑定 button.name 获取名称值 -->
<div class="menu-name">{{button.name}}</div>
<!-- 删除菜单按钮点击时通过 $emit 触发父组件的 'delMenu' 事件由父组件来处理菜单删除相关逻辑 -->
<div class="menu-del" @click="$emit('delMenu')"></div>
</div>
<!-- 菜单名称的输入框组 -->
<!-- 菜单输入组容器用于输入菜单名称 -->
<div class="menu-input-group">
<!-- 菜单名称标签 -->
<div class="menu-label">菜单名称</div>
<!-- 菜单名称输入框所在的容器 -->
<div class="menu-input">
<!-- 输入框用于输入菜单名称绑定到button.name并监听输入事件 -->
<input type="text" name="name" placeholder="请输入菜单名称" class="menu-input-text" v-model="button.name"
@input="checkMenuName(button.name)">
<!-- 当菜单名称超过字数限制时显示 -->
<!-- 文本输入框用于输入菜单名称绑定 v-model button.name 实现双向数据绑定监听 input 事件触发 checkMenuName 方法来检查名称长度是否合规 -->
<input type="text" name="name" placeholder="请输入菜单名称" class="menu-input-text" v-model="button.name" @input="checkMenuName(button.name)">
<!-- 提示信息 menuNameBounds true 时显示提示字数超过上限通过 v-show 根据条件控制显示与否 -->
<p class="menu-tips" style="color:#e15f63" v-show="menuNameBounds"></p>
<!-- 显示菜单名称的字数限制 -->
<p class="menu-tips">字数不超过{{ selectedMenuLevel == 1 ? '5' : '8' }}个汉字</p>
<!-- 提示信息显示菜单名称的字数限制规则根据 selectedMenuLevel 的值动态展示不同的字数上限说明 -->
<p class="menu-tips">字数不超过{{selectedMenuLevel==1?'5':'8'}}个汉字</p>
</div>
</div>
<!-- 当没有子菜单或子菜单为空时显示 -->
<div v-show="!button.subButtons || button.subButtons.length == 0">
<!-- 菜单内容的输入框组 -->
<!-- 根据按钮是否有子按钮或者子按钮数组长度是否为 0 来决定是否显示下面的内容若没有子按钮则展示以下配置项 -->
<div v-show="!button.subButtons || button.subButtons.length==0">
<!-- 菜单输入组容器用于选择菜单内容类型 -->
<div class="menu-input-group">
<!-- 菜单内容类型标签 -->
<div class="menu-label">菜单内容</div>
<!-- 下拉选择框用于选择菜单的类型通过 v-model 双向绑定 button.type绑定不同的选项值每个选项对应不同的菜单功能 -->
<div class="menu-input">
<!-- 下拉选择框用于选择菜单类型 -->
<select v-model="button.type" name="type" class="menu-input-text">
<option value="view">跳转网页(view)</option>
<option value="media_id">发送消息(media_id)</option>
<!-- 注释掉的选项 -->
<!--<option value="view_limited">跳转公众号图文消息链接(view_limited)</option>-->
<option value="miniprogram">打开指定小程序(miniprogram)</option>
<option value="click">自定义点击事件(click)</option>
@ -45,55 +45,83 @@
</select>
</div>
</div>
<!-- 根据选择的菜单类型显示不同的内容输入框 -->
<div class="menu-content" v-if="button.type == 'view'">
<!-- 当菜单类型为 'view' 时显示的内容用于配置跳转网页相关信息 -->
<div class="menu-content" v-if="button.type=='view'">
<!-- 菜单输入组容器用于输入页面地址 -->
<div class="menu-input-group">
<!-- 提示信息说明点击该子菜单后的跳转行为 -->
<p class="menu-tips">订阅者点击该子菜单会跳到以下链接</p>
<!-- 页面地址标签 -->
<div class="menu-label">页面地址</div>
<!-- 页面地址输入框所在的容器 -->
<div class="menu-input">
<!-- 文本输入框用于输入页面地址通过 v-model 双向绑定 button.url -->
<input type="text" placeholder="" class="menu-input-text" v-model="button.url">
</div>
</div>
</div>
<div class="menu-content" v-else-if="button.type == 'media_id'">
<!-- 当菜单类型为 'media_id' 时显示的内容用于配置发送图文消息相关信息 -->
<div class="menu-content" v-else-if="button.type=='media_id'">
<!-- 菜单输入组容器用于输入图文消息的 media_id -->
<div class="menu-input-group">
<!-- 提示信息说明点击该菜单后的行为 -->
<p class="menu-tips">订阅者点击该菜单会收到以下图文消息</p>
<!-- media_id 标签 -->
<div class="menu-label">media_id</div>
<!-- media_id 输入框所在的容器 -->
<div class="menu-input">
<!-- 文本输入框用于输入图文消息的 media_id通过 v-model 双向绑定 button.mediaId -->
<input type="text" placeholder="图文消息media_id" class="menu-input-text" v-model="button.mediaId">
</div>
</div>
</div>
<div class="menu-content" v-else-if="button.type == 'miniprogram'">
<!-- 当菜单类型为 'miniprogram' 时显示的内容用于配置小程序相关信息包含 appId页面路径以及备用网页等 -->
<div class="menu-content" v-else-if="button.type=='miniprogram'">
<!-- 菜单输入组容器用于输入小程序的 appId -->
<div class="menu-input-group">
<!-- 提示信息说明点击该子菜单后的跳转行为 -->
<p class="menu-tips">订阅者点击该子菜单会跳到以下小程序</p>
<!-- 小程序 appId 标签 -->
<div class="menu-label">小程序appId</div>
<!-- 小程序 appId 输入框所在的容器 -->
<div class="menu-input">
<input type="text" placeholder="小程序的appId仅认证公众号可配置" class="menu-input-text"
v-model="button.appId">
<!-- 文本输入框用于输入小程序的 appId通过 v-model 双向绑定 button.appId并提示仅认证公众号可配置 -->
<input type="text" placeholder="小程序的appId仅认证公众号可配置" class="menu-input-text" v-model="button.appId">
</div>
</div>
<!-- 菜单输入组容器用于输入小程序的页面路径 -->
<div class="menu-input-group">
<!-- 小程序路径标签 -->
<div class="menu-label">小程序路径</div>
<!-- 小程序路径输入框所在的容器 -->
<div class="menu-input">
<input type="text" placeholder="小程序的页面路径 pages/index/index" class="menu-input-text"
v-model="button.pagePath">
<!-- 文本输入框用于输入小程序的页面路径通过 v-model 双向绑定 button.pagePath并给出示例路径 -->
<input type="text" placeholder="小程序的页面路径 pages/index/index" class="menu-input-text" v-model="button.pagePath">
</div>
</div>
<!-- 菜单输入组容器用于输入备用网页地址 -->
<div class="menu-input-group">
<!-- 备用网页标签 -->
<div class="menu-label">备用网页</div>
<!-- 备用网页地址输入框所在的容器 -->
<div class="menu-input">
<!-- 文本输入框用于输入备用网页地址通过 v-model 双向绑定 button.url并给出相关说明 -->
<input type="text" placeholder="" class="menu-input-text" v-model="button.url">
<p class="menu-tips">旧版微信客户端无法支持小程序用户点击菜单时将会打开备用网页</p>
</div>
</div>
</div>
<!-- 当菜单类型为其他值非上述几种情况时显示的内容用于配置菜单 KEY -->
<div class="menu-content" v-else>
<!-- 菜单输入组容器用于输入菜单 KEY -->
<div class="menu-input-group">
<!-- 提示信息说明 KEY 值的长度限制及用途 -->
<p class="menu-tips">用于消息接口推送不超过128字节</p>
<!-- 菜单 KEY 值标签 -->
<div class="menu-label">菜单KEY值</div>
<!-- 菜单 KEY 值输入框所在的容器 -->
<div class="menu-input">
<!-- 文本输入框用于输入菜单 KEY 通过 v-model 双向绑定 button.key -->
<input type="text" placeholder="" class="menu-input-text" v-model="button.key">
</div>
</div>
@ -104,13 +132,14 @@
<script>
export default {
//
props: {
// 1
// 1
selectedMenuLevel: {
type: Number,
default: 1
},
//
//
button: {
type: Object,
required: true
@ -118,29 +147,35 @@ export default {
},
data() {
return {
//
// false
menuNameBounds: false,
}
},
methods: {
//
checkMenuName: function (val) {
// menuNameBounds
if (this.selectedMenuLevel == 1 && this.getMenuNameLen(val) <= 5) { // 510
this.menuNameBounds = false
} else if (this.selectedMenuLevel == 2 && this.getMenuNameLen(val) <= 8) {
this.menuNameBounds = false
} else {
this.menuNameBounds = true
// 1 getMenuNameLen 10
if (this.selectedMenuLevel == 1 && this.getMenuNameLen(val) <= 10) {
// menuNameBounds false
this.menuNameBounds = false;
}
// 2 16
else if (this.selectedMenuLevel == 2 && this.getMenuNameLen(val) <= 16) {
this.menuNameBounds = false;
}
// menuNameBounds true
else {
this.menuNameBounds = true;
}
},
//
//
getMenuNameLen: function (val) {
var len = 0;
//
for (var i = 0; i < val.length; i++) {
var a = val.charAt(i);
//
a.match(/[^\x00-\xff]/ig) != null ? len += 2 : len += 1;
// ASCII 2 1
a.match(/[^\x00-\xff]/ig)!= null? len += 2 : len += 1;
}
return len;
}

Loading…
Cancel
Save