Merge remote-tracking branch 'yudao/dev' into dev
This commit is contained in:
@@ -16,12 +16,6 @@ export namespace MpAccountApi {
|
||||
remark?: string;
|
||||
createTime?: Date;
|
||||
}
|
||||
|
||||
// TODO @dylan:这个直接使用 Account,简化一点;
|
||||
export interface AccountSimple {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询公众号账号列表 */
|
||||
@@ -41,7 +35,7 @@ export function getAccount(id: number) {
|
||||
|
||||
/** 查询公众号账号列表 */
|
||||
export function getSimpleAccountList() {
|
||||
return requestClient.get<MpAccountApi.AccountSimple[]>(
|
||||
return requestClient.get<MpAccountApi.Account[]>(
|
||||
'/mp/account/list-all-simple',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
// TODO @dylan:这个类的代码,应该和对应的 antd 是一致的。调整下~看看相关的 vue 是不是也要调整掉。
|
||||
/** 消息类型枚举 */
|
||||
export enum MessageType {
|
||||
IMAGE = 'image', // 图片消息
|
||||
|
||||
@@ -145,8 +145,8 @@ export function useApiSelect(option: ApiSelectProps) {
|
||||
}
|
||||
|
||||
return {
|
||||
label: label,
|
||||
value: value,
|
||||
label,
|
||||
value,
|
||||
};
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -202,10 +202,10 @@ export async function useFormCreateDesigner(designer: Ref) {
|
||||
value: 'id',
|
||||
options: [
|
||||
{ label: '部门编号', value: 'id' },
|
||||
{ label: '部门名称', value: 'name' }
|
||||
]
|
||||
}
|
||||
]
|
||||
{ label: '部门名称', value: 'name' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
const dictSelectRule = useDictSelectRule();
|
||||
const apiSelectRule0 = useSelectRule({
|
||||
|
||||
@@ -1,65 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import type { BasicOption } from '@vben/types';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { SystemUserProfileApi } from '#/api/system/user/profile';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { ProfileBaseSetting } from '@vben/common-ui';
|
||||
import { ProfileBaseSetting, z } from '@vben/common-ui';
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getUserInfoApi } from '#/api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import { updateUserProfile } from '#/api/system/user/profile';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = defineProps<{
|
||||
profile?: SystemUserProfileApi.UserProfileRespVO;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'success'): void;
|
||||
}>();
|
||||
|
||||
const profileBaseSettingRef = ref();
|
||||
|
||||
const MOCK_ROLES_OPTIONS: BasicOption[] = [
|
||||
{
|
||||
label: '管理员',
|
||||
value: 'super',
|
||||
},
|
||||
{
|
||||
label: '用户',
|
||||
value: 'user',
|
||||
},
|
||||
{
|
||||
label: '测试',
|
||||
value: 'test',
|
||||
},
|
||||
];
|
||||
|
||||
const formSchema = computed((): VbenFormSchema[] => {
|
||||
return [
|
||||
{
|
||||
fieldName: 'realName',
|
||||
label: '用户昵称',
|
||||
fieldName: 'nickname',
|
||||
component: 'Input',
|
||||
label: '姓名',
|
||||
},
|
||||
{
|
||||
fieldName: 'username',
|
||||
component: 'Input',
|
||||
label: '用户名',
|
||||
},
|
||||
{
|
||||
fieldName: 'roles',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
mode: 'tags',
|
||||
options: MOCK_ROLES_OPTIONS,
|
||||
placeholder: '请输入用户昵称',
|
||||
},
|
||||
label: '角色',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'introduction',
|
||||
component: 'Textarea',
|
||||
label: '个人简介',
|
||||
label: '用户手机',
|
||||
fieldName: 'mobile',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入用户手机',
|
||||
},
|
||||
rules: z.string(),
|
||||
},
|
||||
{
|
||||
label: '用户邮箱',
|
||||
fieldName: 'email',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入用户邮箱',
|
||||
},
|
||||
rules: z.string().email('请输入正确的邮箱'),
|
||||
},
|
||||
{
|
||||
label: '用户性别',
|
||||
fieldName: 'sex',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: z.number(),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await getUserInfoApi();
|
||||
profileBaseSettingRef.value.getFormApi().setValues(data);
|
||||
});
|
||||
async function handleSubmit(values: Recordable<any>) {
|
||||
try {
|
||||
profileBaseSettingRef.value.getFormApi().setLoading(true);
|
||||
// 提交表单
|
||||
await updateUserProfile(values as SystemUserProfileApi.UpdateProfileReqVO);
|
||||
// 关闭并提示
|
||||
emit('success');
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
profileBaseSettingRef.value.getFormApi().setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 监听 profile 变化 */
|
||||
watch(
|
||||
() => props.profile,
|
||||
(newProfile) => {
|
||||
if (newProfile) {
|
||||
profileBaseSettingRef.value.getFormApi().setValues(newProfile);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" />
|
||||
<ProfileBaseSetting
|
||||
ref="profileBaseSettingRef"
|
||||
:form-schema="formSchema"
|
||||
@submit="handleSubmit"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -274,9 +274,9 @@ onMounted(async () => {
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<div class="border-border bg-muted mt-2 rounded border p-2">
|
||||
<div class="text-muted-foreground flex justify-between text-sm">
|
||||
<span class="text-foreground font-medium">合计:</span>
|
||||
<div class="mt-2 rounded border border-border bg-muted p-2">
|
||||
<div class="flex justify-between text-sm text-muted-foreground">
|
||||
<span class="font-medium text-foreground">合计:</span>
|
||||
<div class="flex space-x-4">
|
||||
<span>数量:{{ erpCountInputFormatter(summaries.count) }}</span>
|
||||
<span>
|
||||
|
||||
@@ -294,9 +294,9 @@ onMounted(async () => {
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<div class="border-border bg-muted mt-2 rounded border p-2">
|
||||
<div class="text-muted-foreground flex justify-between text-sm">
|
||||
<span class="text-foreground font-medium">合计:</span>
|
||||
<div class="mt-2 rounded border border-border bg-muted p-2">
|
||||
<div class="flex justify-between text-sm text-muted-foreground">
|
||||
<span class="font-medium text-foreground">合计:</span>
|
||||
<div class="flex space-x-4">
|
||||
<span>数量:{{ erpCountInputFormatter(summaries.count) }}</span>
|
||||
<span>
|
||||
|
||||
@@ -273,8 +273,8 @@ onMounted(async () => {
|
||||
|
||||
<template #bottom>
|
||||
<div class="mt-2 rounded border border-border bg-muted p-2">
|
||||
<div class="text-muted-foreground flex justify-between text-sm">
|
||||
<span class="text-foreground font-medium">合计:</span>
|
||||
<div class="flex justify-between text-sm text-muted-foreground">
|
||||
<span class="font-medium text-foreground">合计:</span>
|
||||
<div class="flex space-x-4">
|
||||
<span>数量:{{ erpCountInputFormatter(summaries.count) }}</span>
|
||||
<span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export { default as WxAccountSelect } from './wx-account-select/wx-account-select.vue';
|
||||
export { default as WxLocation } from './wx-location/wx-location.vue';
|
||||
export { default as WxMaterialSelect } from './wx-material-select/wx-material-select.vue';
|
||||
export { default as WxMsg } from './wx-msg/msg.vue';
|
||||
export { default as WxMsg } from './wx-msg/msg.vue'; // TODO @hw、@dylan:貌似和 antd 不同。antd 这里是 export { default as WxMsg } from './wx-msg/wx-msg.vue'; 看看哪个是对的
|
||||
export { default as WxMusic } from './wx-music/wx-music.vue';
|
||||
export { default as WxNews } from './wx-news/wx-news.vue';
|
||||
export { default as WxReply } from './wx-reply/wx-reply.vue';
|
||||
|
||||
@@ -10,9 +10,11 @@ import { ElCol, ElLink, ElMessage, ElRow } from 'element-plus';
|
||||
import { getTradeConfig } from '#/api/mall/trade/config';
|
||||
|
||||
/** 微信消息 - 定位 */
|
||||
defineOptions({ name: 'Location' });
|
||||
defineOptions({ name: 'WxLocation' });
|
||||
|
||||
const props = defineProps<WxLocationProps>();
|
||||
const props = withDefaults(defineProps<WxLocationProps>(), {
|
||||
qqMapKey: '', // QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
||||
});
|
||||
|
||||
const fetchedQqMapKey = ref('');
|
||||
const resolvedQqMapKey = computed(
|
||||
|
||||
@@ -46,13 +46,15 @@ function getNickname(sendFrom: number) {
|
||||
</div>
|
||||
<div class="relative mx-2 flex-1 rounded-[5px] border border-[#dedede]">
|
||||
<span
|
||||
class="pointer-events-none absolute -left-2 top-[10px] h-0 w-0 border-y-[8px] border-r-[8px] border-y-transparent border-r-[#dedede]"
|
||||
v-if="item.sendFrom === SendFrom.MpBot"
|
||||
class="pointer-events-none absolute -left-2 top-[10px] h-0 w-0 border-y-[8px] border-r-[8px] border-y-transparent border-r-[transparent]"
|
||||
:class="{
|
||||
'-right-2 left-auto border-l-[8px] border-r-0 border-l-[#dedede]':
|
||||
item.sendFrom === SendFrom.MpBot,
|
||||
}"
|
||||
></span>
|
||||
<span
|
||||
v-if="item.sendFrom === SendFrom.User"
|
||||
class="pointer-events-none absolute -left-[7px] top-[10px] h-0 w-0 border-y-[8px] border-r-[8px] border-y-transparent border-r-[#f8f8f8]"
|
||||
:class="{
|
||||
'-right-[7px] left-auto border-l-[8px] border-r-0 border-l-[#f8f8f8]':
|
||||
|
||||
@@ -18,7 +18,7 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<div class="mx-auto flex w-full flex-col gap-[10px] bg-white">
|
||||
<div v-for="(article, index) in articles" :key="index" class="news-div">
|
||||
<div v-for="(article, index) in articles" :key="index">
|
||||
<!-- 头条 -->
|
||||
<a v-if="index === 0" :href="article.url" target="_blank">
|
||||
<div class="mx-auto w-full">
|
||||
@@ -26,11 +26,10 @@ defineExpose({
|
||||
<img
|
||||
:src="article.picUrl"
|
||||
:preview="false"
|
||||
class="flex w-[100%] items-center justify-center object-cover"
|
||||
class="w-[100px] object-cover"
|
||||
/>
|
||||
<div
|
||||
class="absolute bottom-0 left-0 ml-[10px] inline-block w-[98%] whitespace-normal p-[1%] text-base text-white"
|
||||
style="box-sizing: unset !important"
|
||||
>
|
||||
<span>{{ article.title }}</span>
|
||||
</div>
|
||||
|
||||
@@ -78,8 +78,6 @@ function onDelete() {
|
||||
/** 选择素材 */
|
||||
function selectMaterial(item: any) {
|
||||
showDialog.value = false;
|
||||
|
||||
// reply.value.type = 'image'
|
||||
reply.value.mediaId = item.mediaId;
|
||||
reply.value.url = item.url;
|
||||
reply.value.name = item.name;
|
||||
|
||||
@@ -8,8 +8,7 @@ import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElButton, ElCol, ElDialog, ElRow } from 'element-plus';
|
||||
|
||||
import MaterialSelect from '#/views/mp/components/wx-material-select/wx-material-select.vue';
|
||||
import News from '#/views/mp/components/wx-news/wx-news.vue';
|
||||
import { WxMaterialSelect, WxNews } from '#/views/mp/components';
|
||||
|
||||
defineOptions({ name: 'TabNews' });
|
||||
|
||||
@@ -48,7 +47,7 @@ function onDelete() {
|
||||
class="mx-auto mb-[10px] w-[280px] border border-[#eaeaea] p-[10px]"
|
||||
v-if="reply.articles && reply.articles.length > 0"
|
||||
>
|
||||
<News :articles="reply.articles" />
|
||||
<WxNews :articles="reply.articles" />
|
||||
<ElCol class="pt-[10px] text-center">
|
||||
<ElButton type="danger" circle @click="onDelete">
|
||||
<IconifyIcon icon="lucide:trash-2" />
|
||||
@@ -78,7 +77,7 @@ function onDelete() {
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<MaterialSelect
|
||||
<WxMaterialSelect
|
||||
type="news"
|
||||
:account-id="reply.accountId"
|
||||
:news-type="newsType"
|
||||
|
||||
@@ -3,8 +3,7 @@ import { ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
// 因为微信语音是 amr 格式,所以需要用到 amr 解码器:https://www.npmjs.com/package/benz-amr-recorder
|
||||
import BenzAMRRecorder from 'benz-amr-recorder';
|
||||
import BenzAMRRecorder from 'benz-amr-recorder'; // 因为微信语音是 amr 格式,所以需要用到 amr 解码器:https://www.npmjs.com/package/benz-amr-recorder
|
||||
import { ElTag } from 'element-plus';
|
||||
|
||||
/** 微信消息 - 语音 */
|
||||
@@ -64,23 +63,24 @@ function amrStop() {
|
||||
playing.value = false;
|
||||
amr.value.stop();
|
||||
}
|
||||
// TODO dylan:下面样式有点问题
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex min-h-[50px] min-w-[120px] flex-col items-center justify-center rounded-[10px] bg-[#eaeaea] px-3 py-2"
|
||||
class="flex h-[50px] w-[120px] cursor-pointer items-center justify-center rounded-[10px] bg-[#eaeaea] p-[5px]"
|
||||
@click="playVoice"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<el-icon>
|
||||
<IconifyIcon
|
||||
v-if="playing !== true"
|
||||
icon="lucide:circle-play"
|
||||
:size="32"
|
||||
/>
|
||||
<IconifyIcon v-else icon="lucide:circle-pause" :size="32" />
|
||||
<span class="ml-2 text-xs" v-if="duration">{{ duration }} 秒</span>
|
||||
</div>
|
||||
<span v-if="duration" class="ml-[5px] text-[11px]">
|
||||
{{ duration }} 秒
|
||||
</span>
|
||||
</el-icon>
|
||||
<div v-if="content">
|
||||
<ElTag type="success" size="small">语音识别</ElTag>
|
||||
{{ content }}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { getSimpleAccountList } from '#/api/mp/account';
|
||||
|
||||
let accountList: MpAccountApi.AccountSimple[] = [];
|
||||
let accountList: MpAccountApi.Account[] = [];
|
||||
getSimpleAccountList().then((data) => (accountList = data));
|
||||
|
||||
/** 搜索表单配置 */
|
||||
|
||||
Reference in New Issue
Block a user