From 2581f9ae47ed8342b1494bc84f4236dcedb66fbc Mon Sep 17 00:00:00 2001
From: 陶杰 <1378534974@qq.com>
Date: 星期日, 29 十二月 2024 13:58:29 +0800
Subject: [PATCH] 1.短信模板、短信批量任务
---
pages/sms/send-batch/_id.vue | 135 +++++++
pages/sms/send-batch/index.vue | 349 +++++++++++++++++++
components/sms/select-all-user.vue | 244 +++++++++++++
components/sms/template-download.vue | 80 ++++
pages/sms/template/index.vue | 123 ++++++
components/sms/copy-textarea.vue | 92 +++++
6 files changed, 1,023 insertions(+), 0 deletions(-)
diff --git a/components/sms/copy-textarea.vue b/components/sms/copy-textarea.vue
new file mode 100644
index 0000000..16e3b90
--- /dev/null
+++ b/components/sms/copy-textarea.vue
@@ -0,0 +1,92 @@
+<template>
+ <div class="copy-textarea">
+ <div>
+ <span style="color:red;">手动输入最多支持100个号码,大批量号码建议通过文件导入形式提交</span>
+ <el-button type="text" @click="clearVal">点击清空</el-button></div>
+ <el-input type="textarea" :rows="5" v-model="currentValue"
+ placeholder="提示:一行输入一个号码,多个手机号请换行隔开。"
+ width="80%"
+ @change="handlerInputChange"
+ ></el-input>
+ <div>
+ <span style="color:gray;">提示:一行输入一个号码,多个手机号请换行隔开。</span>
+ </div>
+
+ </div>
+</template>
+
+<script>
+import cloneDeep from 'lodash.clonedeep'
+export default {
+ props: {
+ value: {
+ type: String,
+ default:'',
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ dialogVisible: false,
+ currentValue: '',
+ }
+ },
+ watch: {
+ value: {
+ immediate: true,
+ handler(value) {
+ this.currentValue = value
+ },
+ },
+ },
+ methods: {
+ clearVal(){
+ this.$elBusUtil
+ .confirm('确定要清空吗?')
+ .then(() => {
+ this.currentValue = ''
+ this.$emit('input', '')
+ this.$emit('change', '')
+ })
+ .catch(() => {})
+ },
+ handlerInputChange(){
+ this.$emit('input', this.currentValue)
+ this.$emit('change', this.currentValue)
+ }
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.copy-textarea {
+}
+</style>
+<style lang="scss">
+.shop-user-dialog {
+ .dialog-container {
+ display: flex;
+ align-items: flex-start;
+ &__list {
+ flex: 1;
+ border-right: 1px solid #eee;
+ height: 100%;
+ }
+ &__selected {
+ width: 40%;
+ height: 100%;
+ padding: 24px;
+ .el-bus-title {
+ margin-bottom: 15px;
+ }
+ .el-tag {
+ margin-right: 6px;
+ margin-bottom: 6px;
+ }
+ }
+ }
+}
+</style>
diff --git a/components/sms/select-all-user.vue b/components/sms/select-all-user.vue
new file mode 100644
index 0000000..bc8b325
--- /dev/null
+++ b/components/sms/select-all-user.vue
@@ -0,0 +1,244 @@
+<template>
+ <div class="select-shop-user">
+ <el-button v-if="!disabled" type="primary" @click="chooseUser">选择用户列表</el-button>
+ <el-table v-if="value && value.length > 0" :data="value">
+ <el-table-column label="id" prop="id"></el-table-column>
+ <el-table-column label="名称" prop="loginName"></el-table-column>
+ <el-table-column label="注册手机号方式" prop="tel"></el-table-column>
+ <el-table-column v-if="!disabled" label="操作">
+ <template #default="{ $index }">
+ <text-button type="danger" @click="deleteUser($index)">删除</text-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <el-dialog :visible.sync="dialogVisible" title="选择用户列表" append-to-body :close-on-click-modal="false"
+ custom-class="shop-user-dialog" width="80%">
+ <div class="dialog-container">
+ <div class="dialog-container__list">
+ <el-bus-crud v-bind="tableConfig" />
+ </div>
+ <div class="dialog-container__selected">
+ <el-bus-title title="已添加用户" size="mini" />
+ <el-tag v-for="(tag, i) in currentValue" :key="tag.id" closable @close="deleteCurrentUser(i)">{{ tag.loginName
+ }}</el-tag>
+ </div>
+ </div>
+ <div slot="footer">
+ <el-button @click="cancel">取消</el-button>
+ <el-button type="primary" @click="confirm">确定</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import cloneDeep from 'lodash.clonedeep'
+export default {
+ props: {
+ value: {
+ type: Array,
+ default: () => [],
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ dialogVisible: false,
+ currentValue: [],
+ tableConfig: {
+ url: 'flower/api/user/user/list',
+ saveQuery: false,
+ hasNew: false,
+ hasEdit: false,
+ hasDelete: false,
+ hasView: false,
+ columns: [
+ { type: 'selection' },
+ { label: 'id', prop: 'id' },
+ { label: '名称', prop: 'loginName' },
+ { label: '注册手机号', prop: 'tel' },
+ ],
+ extraButtons: [
+ {
+ text: '选择',
+ show: (row) =>
+ !this.currentValue.find((item) => item.id === row.id),
+ atClick: (row) => {
+ this.currentValue.push({
+ id: row.id,
+ loginName: row.loginName,
+ tel: row.tel,
+ })
+ return false
+ },
+ },
+ {
+ text: '取消选择',
+ show: (row) => this.currentValue.find((item) => item.id === row.id),
+ atClick: (row) => {
+ const index = this.currentValue.findIndex(
+ (item) => item.id === row.id
+ )
+ this.currentValue.splice(index, 1)
+ return false
+ },
+ },
+ ],
+ headerButtons: [
+ {
+ text: '批量选择',
+ type: 'primary',
+ disabled: (selected) => selected.length === 0,
+ atClick: (selected) => {
+ console.log(selected)
+ selected.forEach(item => {
+ // 检查 selected 数组的每个元素是否已在 currentValue 中
+ if (!this.currentValue.some(currentItem => currentItem.id === item.id)) {
+ this.currentValue.push({
+ id: item.id,
+ loginName: item.loginName,
+ tel: item.tel,
+ })
+ }
+ });
+ return true
+ },
+ },
+ {
+ text: '批量取消',
+ type: 'primary',
+ disabled: (selected) => selected.length === 0,
+ atClick: (selected) => {
+ selected.forEach(item => {
+ // 检查 selected 数组的每个元素是否存在于 currentValue 中
+ const index = this.currentValue.findIndex(currentItem => currentItem.id === item.id);
+ if (index !== -1) {
+ this.currentValue.splice(index, 1); // 如果存在,则移除
+ }
+ });
+ return true
+ },
+ },
+ ],
+
+ searchFormAttrs: {
+ labelWidth: 'auto',
+ },
+ searchForm: [
+ {
+ type: 'row',
+ span: 12,
+ items: [
+ {
+ label: '列表类型:',
+ id: 'userType',
+ type: 'bus-select-dict',
+ el: {
+ code: 'USER_TYPE',
+ multiple: false,
+ style: 'width:100%',
+ },
+ default: 'customer',
+ searchImmediately: true,
+ on: {
+ change: (e, updateForm, obj) => {
+ console.log(e[0])
+ // if (e[0] === 'supplier') {
+ // this.tableConfig.url = 'flower/api/supplier/page'
+ // } else if (e[0] === 'partner') {
+ // this.tableConfig.url = 'flower/api/partner/page'
+ // }else if(e[0]==='customer'){
+ // this.tableConfig.url = 'flower/api/customer/page'
+ // }
+ },
+ },
+
+ },
+
+ { label: 'id:', id: 'id', type: 'input' },
+ { label: '名称:', id: 'loginName', type: 'input' },
+ { label: '注册手机号:', id: 'tel', type: 'input' },
+ ],
+ },
+ ],
+ },
+ }
+ },
+ watch: {
+ value: {
+ immediate: true,
+ handler(value) {
+ this.currentValue = cloneDeep(value || [])
+ },
+ },
+ },
+ methods: {
+ handleSelectionChange(rows) {
+ console.log(rows)
+ alert("全选")
+ },
+ chooseUser() {
+ this.currentValue = cloneDeep(this.value || [])
+ this.dialogVisible = true
+ },
+ deleteCurrentUser(i) {
+ this.currentValue.splice(i, 1)
+ },
+ deleteUser(i) {
+ this.$elBusUtil
+ .confirm('确定要删除吗?')
+ .then(() => {
+ const userList = cloneDeep(this.value || [])
+ userList.splice(i, 1)
+ this.$emit('input', userList)
+ this.$emit('change', userList)
+ })
+ .catch(() => { })
+ },
+ confirm() {
+ this.$emit('input', this.currentValue)
+ this.$emit('change', this.currentValue)
+ this.dialogVisible = false
+ },
+ cancel() {
+ this.dialogVisible = false
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.select-shop-user {}
+</style>
+<style lang="scss">
+.shop-user-dialog {
+ .dialog-container {
+ display: flex;
+ align-items: flex-start;
+
+ &__list {
+ flex: 1;
+ border-right: 1px solid #eee;
+ height: 100%;
+ }
+
+ &__selected {
+ width: 40%;
+ height: 100%;
+ padding: 24px;
+
+ .el-bus-title {
+ margin-bottom: 15px;
+ }
+
+ .el-tag {
+ margin-right: 6px;
+ margin-bottom: 6px;
+ }
+ }
+ }
+}
+</style>
diff --git a/components/sms/template-download.vue b/components/sms/template-download.vue
new file mode 100644
index 0000000..1b16e26
--- /dev/null
+++ b/components/sms/template-download.vue
@@ -0,0 +1,80 @@
+<template>
+ <div class="copy-textarea">
+ <div>
+ <el-link href="https://hmy-flower.oss-cn-shanghai.aliyuncs.com/a5/a57ec65b165148e5a669e7766743e489template_phone.xlsx" download="template_phone.xlsx">
+ <span style="color:#5FA7EE;">点击下载模板</span>
+ </el-link>
+
+ <!-- <el-link @click="downloadTemplate">点击下载模板a</el-link> -->
+ </div>
+
+ </div>
+</template>
+
+<script>
+import cloneDeep from 'lodash.clonedeep'
+export default {
+ props: {
+ value: {
+ type: String,
+ default:'',
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ dialogVisible: false,
+ currentValue: '',
+ }
+ },
+ watch: {
+ value: {
+ immediate: true,
+ handler(value) {
+ this.currentValue = value
+ },
+ },
+ },
+ methods: {
+ downloadTemplate() {
+ const link = document.createElement('a');
+ link.href = 'https://hmy-flower.oss-cn-shanghai.aliyuncs.com/a5/a57ec65b165148e5a669e7766743e489template_phone.xlsx';
+ link.download = 'template_phone.xlsx';
+ link.click();
+ }
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.copy-textarea {
+}
+</style>
+<style lang="scss">
+.shop-user-dialog {
+ .dialog-container {
+ display: flex;
+ align-items: flex-start;
+ &__list {
+ flex: 1;
+ border-right: 1px solid #eee;
+ height: 100%;
+ }
+ &__selected {
+ width: 40%;
+ height: 100%;
+ padding: 24px;
+ .el-bus-title {
+ margin-bottom: 15px;
+ }
+ .el-tag {
+ margin-right: 6px;
+ margin-bottom: 6px;
+ }
+ }
+ }
+}
+</style>
diff --git a/pages/sms/send-batch/_id.vue b/pages/sms/send-batch/_id.vue
new file mode 100644
index 0000000..c2f9e54
--- /dev/null
+++ b/pages/sms/send-batch/_id.vue
@@ -0,0 +1,135 @@
+<template>
+ <div class="base-page-wrapper coupon-detail">
+ <el-bus-title title="基本信息" size="small" />
+ <!-- <el-bus-form ref="form" label-width="auto" :content="formContent" readonly class="readonly-form" /> -->
+ <el-form ref="form" label-width="100px">
+ <el-form-item label="模板名称:"> {{ statisticsData.smsTaskName || '' }} </el-form-item>
+ <el-form-item label="号码数量:"> {{ statisticsData.totalNum || 0 }} </el-form-item>
+ </el-form>
+
+ <div class="base-page-wrapper__line"></div>
+ <el-bus-title title="发送结果" size="small" />
+ <div><el-row :gutter="20">
+ <el-col :span="3" class="mb-2">
+ <el-card>
+ <div class="statistic-title">发送号码数量</div>
+ <div class="statistic-num">{{ statisticsData.totalNum || 0 }}</div>
+ </el-card>
+ </el-col>
+ <el-col :span="3" class="mb-2">
+ <el-card>
+ <div class="statistic-title">成功</div>
+ <div class="statistic-num">{{ statisticsData.successNum || 0 }}</div>
+ </el-card>
+ </el-col>
+ <el-col :span="3" class="mb-2">
+ <el-card>
+ <div class="statistic-title">失败</div>
+ <div class="statistic-num">{{ statisticsData.failureNum || 0 }}</div>
+ </el-card>
+ </el-col>
+ <el-col :span="3" class="mb-2">
+ <el-card>
+ <div class="statistic-title">发送中</div>
+ <div class="statistic-num"> {{ statisticsData.sendingNum || 0 }} </div>
+ </el-card>
+ </el-col>
+
+ </el-row></div>
+ <div class="base-page-wrapper__line"></div>
+ <el-bus-title title="发放记录" size="small" />
+ <el-bus-crud v-bind="recordTableConfig" />
+ <div class="text-center mt-20">
+ <el-button class="min-w-100" @click="goBack">返回</el-button>
+ </div>
+ </div>
+</template>
+
+<script>
+export default {
+ data() {
+ return {
+ statisticsData:{},
+ recordTableConfig: {
+ url: `flower/v2/sms-task-detail/list`,
+ hasOperation: false,
+ hasNew: false,
+ extraQuery: {
+ smsTaskId: this.$route.params.id,
+ },
+ columns: [
+ { label: '接收号码', prop: 'phone' },
+ { label: '发送时间', prop: 'createTime' },
+ { label: '发送结果', prop: 'resultStr' },
+ { label: '失败原因', prop: 'failReason' },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [
+ { label: '接收号码', id: 'phone', type: 'input', searchImmediately: true, },
+ {
+ label: '发送结果',
+ id: 'result',
+ type: 'bus-select-dict',
+ el: {
+ code: 'SMS_SEND_RESULT',
+ multiple: false,
+ style: 'width:100%',
+ clearable: true,
+ },
+ searchImmediately: true,
+ },
+ ],
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '批量发送短信详情',
+ }
+ },
+ mounted() {
+ this.getInitData()
+ },
+ methods: {
+ async getInitData() {
+ const { code, data } = await this.$elBusHttp.request(
+ `flower/v2/sms-task-detail/taskStatistics/${this.$route.params.id}`,
+ {
+ method: 'get',
+ // params: {
+ // id: this.$route.params.id
+ // }
+ }
+ )
+ if (code === 0) {
+ console.log("data")
+ console.log(data)
+ this.statisticsData = data
+ }
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/coupon/detail.scss';
+@import '@/assets/statistic/index.scss';
+
+.statistic-title {
+ text-align: center;
+ font-size: 16px;
+ color: $main-title-color;
+ font-weight: bold;
+ margin-bottom: 6px;
+}
+
+.statistic-num {
+ text-align: center;
+ font-size: 16px;
+ color: $primary-color;
+}
+</style>
diff --git a/pages/sms/send-batch/index.vue b/pages/sms/send-batch/index.vue
new file mode 100644
index 0000000..692a6af
--- /dev/null
+++ b/pages/sms/send-batch/index.vue
@@ -0,0 +1,349 @@
+<template>
+ <el-bus-crud ref="curd" v-bind="tableConfig" />
+</template>
+
+<script>
+import dayjs from 'dayjs'
+import 'dayjs/locale/zh-cn'
+import SelectAllUser from '@/components/sms/select-all-user'
+import CopyTextarea from '@/components/sms/copy-textarea'
+import TemplateDownload from '@/components/sms/template-download'
+export default {
+ data() {
+ const defaultDate = `${dayjs().format('YYYY-MM-DD')} 00:00:00`
+ return {
+ tableConfig: {
+ url: 'flower/v2/sms-task/list',
+ newUrl: 'flower/v2/sms-task/new',
+ editUrl: 'flower/v2/sms-task/edit',
+ viewUrl: 'flower/v2/sms-task',
+ viewOnPath: true,
+ deleteUrl: 'flower/v2/sms-task/delete',
+ hasNew: true,
+ newText: '添加发送任务',
+ dialogNeedRequest: true,
+ canEdit: (row) => row.status === 'wait_publish',
+ canDelete: (row) => row.status === 'wait_publish',
+ extraButtons: [
+ {
+ text: '发布',
+ show: (row) => {
+ return row.status === 'wait_publish';
+ },
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要发布吗?')
+ const { code } = await this.$elBusHttp.request(
+ `flower/v2/sms-task/publish`,
+ {
+ method: 'post',
+ data: {
+ id: row.id,
+ }
+ }
+ )
+ if (code === 0) {
+ this.$message.success('发布成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ onResetView: (row) => {
+ // this.$router.push(`${this.$route.path}/${row.id}`)
+
+ // const searchFormRef = this.$refs.crud.$refs.searchForm
+ // const searchFormValue = searchFormRef.getFormValue()
+ const url = this.$router.resolve(
+ `/sms/send-batch/${row.id}`
+ ).href
+ window.open(url, '_blank')
+
+ },
+ columns: [
+ { label: '任务名称', prop: 'name' },
+ { label: '模板名称', prop: 'smsTemplateName' },
+ { label: '模板描述', prop: 'smsTemplateDesc' },
+ { label: '任务状态', prop: 'statusStr' },
+ { label: '创建时间', prop: 'createTime' },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [
+ { label: '任务名称', id: 'name', type: 'input' },
+ { label: '模板名称', id: 'smsTemplateName', type: 'input' },
+ {
+ label: '任务状态',
+ id: 'status',
+ type: 'bus-select-dict',
+ el: {
+ code: 'SMS_TASK_STATUS',
+ multiple: false,
+ style: 'width:100%',
+ },
+ },
+ {
+ label: '创建时间',
+ id: 'startDate',
+ component: 'el-bus-date-range',
+ el: {
+ clearable: true,
+ },
+ // commonFormat: true,
+ // commonFormatProps: ['startDate', 'endDate'],
+ inputFormat: (row) => {
+ if ('startDate' in row || 'endDate' in row) {
+ return [
+ this.$elBusUtil.toDate(row.startDate),
+ this.$elBusUtil.toDate(row.endDate),
+ ]
+ }
+ },
+ outputFormat: (val) => {
+ return {
+ startDate: val[0] ? `${this.$elBusUtil.toDate(val[0])} 00:00:00` : undefined,
+ endDate: val[1] ? `${this.$elBusUtil.toDate(val[1])} 23:59:59` : undefined,
+ }
+ },
+ customClass: 'in-bus-form',
+ // commonRules: true,
+ // default: [defaultDate, defaultDate],
+ },
+
+ ],
+ },
+ ],
+ form: [
+ {
+ label: '任务名称',
+ id: 'name',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入任务名称',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '短信模板:',
+ id: 'smsTemplateId',
+ type: 'bus-select',
+ el: {
+ interfaceUri: 'flower/v2/sms-template/templateName/all',
+ extraQuery: {
+ current: 1,
+ size: 2000,
+ },
+ props: {
+ label: 'name',
+ value: 'id',
+ // dataPath: 'data',
+ },
+ filterable: true,
+ multiple: false,
+ style: 'width:100%',
+ },
+ rules: {
+ required: true,
+ message: '请选择短信模板',
+ trigger: 'change',
+ },
+ },
+ {
+ label: '接收号码:',
+ id: 'type',
+ type: 'bus-radio',
+ el: {
+ code: 'SMS_RECEIVE_TYPE',
+ // hasAll: false,
+ // childType: 'el-radio-button',
+ },
+ str: true,
+ on: {
+ change: (e, updateForm, obj) => {
+ // updateForm({
+ // // fileUrl: undefined,
+ // // fileUrlDesc: undefined,
+ // // pointCostomIdList: undefined,
+ // // input: undefined,
+ // })
+ },
+ },
+ default: this.$route.query.status || '',
+ span: 24,
+ searchImmediately: true,
+ rules: {
+ required: true,
+ message: '请输入接收号码',
+ trigger: 'change',
+ },
+ },
+ {
+ label: '导入接收文件',
+ id: 'fileUrl',
+ component: 'el-bus-upload',
+ el: {
+ // listType: 'text',
+ accept: ".xls,.xlsx",
+ limit: 1
+ },
+ commonFormat: true,
+ forceDisabled: true,
+ hidden: (row) => {
+ return row.type !== 'IMPORT'
+ },
+ readonly: false,
+ rules: [
+ { required: true, message: '请上传文件' },
+ {
+ validator: (rule, value, callback) => {
+ if (!value || value.length > 1) {
+ callback(new Error('只能上传一个文件'));
+ } else {
+ callback();
+ }
+ },
+ trigger: 'change',
+ },
+ ],
+ },
+ {
+ label: '',
+ id: 'fileUrlDesc',
+ component: TemplateDownload,
+ hidden: (row) => row.type !== 'IMPORT',
+ },
+ {
+ label: '手动输入:',
+ id: 'phones',
+ component: CopyTextarea,
+ el: {
+ type: 'textarea',
+ },
+ // hidden: (row) => row.discountType !== 'ratio',
+ hidden: (row) => row.type !== 'INPUT',
+ readonly: false,
+ span: 24,
+ rules: [
+ { required: true, message: '请输入号码', trigger: 'blur,change', },
+ // {
+ // validator: (rule, value, callback) => {
+ // // 如果值为空,直接返回错误
+ // if (!value || value.trim() === '') {
+ // callback(new Error('请输入电话号码,每个号码后跟换行符'));
+ // return;
+ // }
+
+ // // 正则表达式:只匹配中国大陆手机号格式
+ // const phoneRegex = /^1[3-9]\d{9}$/;
+ // const lines = value.split('\n'); // 按换行符分割文本
+
+ // // 遍历每一行并校验
+ // for (let i = 0; i < lines.length; i++) {
+ // const line = lines[i]; // 每一行去除空格
+ // if (line && !phoneRegex.test(line)) {
+ // // 如果当前行的电话号码格式不正确,提示出错的行号
+ // callback(new Error(`第 ${i + 1} 行电话号码格式不正确,请检查输入, 如 电话号码后面有空格等`));
+ // return; // 一旦发现不符合的号码格式,就终止校验
+ // }
+ // }
+
+ // if(lines.length>100){
+ // callback(new Error(`手动输入最多支持100个号码`));
+ // }
+
+ // // 所有行都符合格式,执行 callback()
+ // callback();
+ // },
+ // trigger: 'blur',
+ // }
+ {
+ validator: (rule, value, callback) => {
+ // 如果值为空,直接返回错误
+ if (!value || value.trim() === '') {
+ callback(new Error('请输入电话号码,每个号码后跟换行符'));
+ return;
+ }
+
+
+
+ const phoneRegex = /^1[3-9]\d{9}$/; // 校验手机号格式
+ const lines = value.split('\n'); // 按换行符分割文本
+ const phoneCount = {}; // 用来统计电话号码出现次数
+
+ if (lines.length > 100) {
+ callback(new Error(`手动输入最多支持100个号码`));
+ }
+
+ // 校验每一行的电话号码
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i].trim();
+ if (line) {
+ // 如果当前行不是空的,校验其格式
+ if (!phoneRegex.test(line)) {
+ callback(new Error(`第 ${i + 1} 行电话号码格式不正确,请检查输入`));
+ return;
+ }
+
+ // 统计号码出现的次数
+ phoneCount[line] = (phoneCount[line] || 0) + 1;
+ }
+ }
+
+
+ // 收集所有重复的电话号码
+ const duplicates = [];
+ for (const phone in phoneCount) {
+ if (phoneCount[phone] > 1) {
+ duplicates.push(`${phone} 重复了 ${phoneCount[phone]} 次`);
+ }
+ }
+
+
+ // 如果有重复号码,返回错误并列出所有重复的号码
+ if (duplicates.length > 0) {
+ callback(new Error(`以下电话号码重复:\n${duplicates.join('\n')}`));
+ return;
+ }
+
+ // 校验通过
+ callback();
+ },
+ trigger: 'blur',
+ }
+
+ ],
+
+ },
+
+ {
+ label: '选择用户列表',
+ id: 'smsUserDTOS',
+ component: SelectAllUser,
+ hidden: (row) => row.type !== 'SELECT',
+ rules: { required: true, message: '请选择领取用户' },
+ inputFormat: (row) => {
+ if ('smsUserDTOS' in row) {
+ return row.smsUserDTOS.filter((i) => i)
+ }
+ },
+ outputFormat: (val) => {
+ return val?.length ? val.map((i) => {return {userId:i.id,userPhone:i.tel} }) : []
+ },
+ forceDisabled: true,
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '批量发送短信',
+ }
+ },
+
+}
+</script>
diff --git a/pages/sms/template/index.vue b/pages/sms/template/index.vue
new file mode 100644
index 0000000..2db0779
--- /dev/null
+++ b/pages/sms/template/index.vue
@@ -0,0 +1,123 @@
+<template>
+ <el-bus-crud v-bind="tableConfig" />
+</template>
+
+<script>
+import dayjs from 'dayjs'
+import 'dayjs/locale/zh-cn'
+export default {
+ data() {
+ const defaultDate = `${dayjs().format('YYYY-MM-DD')} 00:00:00`
+ return {
+ tableConfig: {
+ url: 'flower/v2/sms-template/list',
+ newUrl: 'flower/v2/sms-template/new',
+ deleteUrl: 'flower/v2/sms-template/delete',
+ editUrl: 'flower/v2/sms-template/edit',
+ columns: [
+ { label: 'ID', prop: 'id' },
+ { label: 'CODE', prop: 'code' },
+ { label: '模板名称', prop: 'name' },
+ { label: '模板描述', prop: 'description' },
+ { label: '创建时间', prop: 'createTime' },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [
+ { label: 'ID', id: 'id', type: 'input' },
+ { label: 'CODE', id: 'code', type: 'input' },
+ { label: '模板名称', id: 'name', type: 'input' },
+ {
+ label: '创建时间',
+ id: 'startDate',
+ component: 'el-bus-date-range',
+ el: {
+ clearable: true,
+ },
+ // commonFormat: true,
+ commonFormatProps: ['startDate', 'endDate'],
+ inputFormat: (row) => {
+ if ('startDate' in row || 'endDate' in row) {
+ return [
+ this.$elBusUtil.toDate(row.startDate),
+ this.$elBusUtil.toDate(row.endDate),
+ ]
+ }
+ },
+ outputFormat: (val) => {
+ return {
+ startDate:val[0] ? `${this.$elBusUtil.toDate(val[0])} 00:00:00`: undefined,
+ endDate: val[1] ?`${this.$elBusUtil.toDate(val[1])} 23:59:59`: undefined,
+ }
+ },
+ customClass: 'in-bus-form',
+ // commonRules: true,
+ // default: [defaultDate, defaultDate],
+ },
+ ],
+ },
+ ],
+ form: [
+ {
+ label: 'CODE:',
+ id: 'code',
+ type: 'bus-select',
+ el: {
+ interfaceUri: 'flower/v2/sms-template/aliyun/list',
+ extraQuery: {
+ current: 1,
+ size: 2000,
+ },
+ props: {
+ label: 'templateName',
+ value: 'templateCode',
+ // dataPath: 'records',
+ },
+ filterable: true,
+ multiple: false,
+ style: 'width:100%',
+ },
+ rules: {
+ required: true,
+ message: '请选择CODE',
+ trigger: 'blur',
+ },
+ },
+
+ {
+ label: '模板名称:',
+ id: 'name',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入模板名称',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '模板描述:',
+ id: 'description',
+ type: 'input',
+ el: {
+ type: 'textarea',
+ rows: 6,
+ },
+ rules: {
+ required: true,
+ message: '请输入模板描述',
+ trigger: 'blur',
+ },
+ },
+
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '短信模板维护',
+ }
+ },
+}
+</script>
--
Gitblit v1.9.3