将行业热门话题洞察的需要修改成columns
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@ -22,6 +22,7 @@
|
|||||||
"sass": "^1.89.2",
|
"sass": "^1.89.2",
|
||||||
"swiper": "^11.2.8",
|
"swiper": "^11.2.8",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
|
"vue-cropper": "^1.1.4",
|
||||||
"vue-echarts": "^7.0.3",
|
"vue-echarts": "^7.0.3",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
@ -8945,6 +8946,12 @@
|
|||||||
"@vue/shared": "3.2.47"
|
"@vue/shared": "3.2.47"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-cropper": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-5m98vBsCEI9rbS4JxELxXidtAui3qNyTHLHg67Qbn7g8cg+E6LcnC+hh3SM/p94x6mFh6KRxT1ttnta+wCYqWA==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/vue-demi": {
|
"node_modules/vue-demi": {
|
||||||
"version": "0.13.11",
|
"version": "0.13.11",
|
||||||
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
|
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||||
|
|||||||
@ -24,11 +24,10 @@
|
|||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"pinia": "^2.0.29",
|
"pinia": "^2.0.29",
|
||||||
|
|
||||||
"sass": "^1.89.2",
|
"sass": "^1.89.2",
|
||||||
|
|
||||||
"swiper": "^11.2.8",
|
"swiper": "^11.2.8",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
|
"vue-cropper": "^1.1.4",
|
||||||
"vue-echarts": "^7.0.3",
|
"vue-echarts": "^7.0.3",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import Http from '@/api';
|
import Http from '@/api';
|
||||||
|
|
||||||
// 导出一个函数,用于获取行业树
|
// 导出一个函数,用于获取行业树
|
||||||
export const fetchIndustriesTree = (params = {}) => {
|
export const fetchIndustriesTree = (params = {}) => {
|
||||||
// 发送GET请求,获取行业树
|
// 发送GET请求,获取行业树
|
||||||
@ -50,7 +49,6 @@ export const fetchNewKeywordDetail = (params: any) => {
|
|||||||
// 使用Http.get方法,发送GET请求,获取行业话题列表
|
// 使用Http.get方法,发送GET请求,获取行业话题列表
|
||||||
return Http.get('/v1/industry-new-keywords/' + params, {});
|
return Http.get('/v1/industry-new-keywords/' + params, {});
|
||||||
};
|
};
|
||||||
fetchIndustryTopicDetail;
|
|
||||||
|
|
||||||
// 导出一个函数fetchUserPainPointsList,用于获取用户痛点列表
|
// 导出一个函数fetchUserPainPointsList,用于获取用户痛点列表
|
||||||
export const fetchUserPainPointsList = (params: any) => {
|
export const fetchUserPainPointsList = (params: any) => {
|
||||||
@ -98,7 +96,7 @@ export const fetchGenderDistributionsList = (params: any) => {
|
|||||||
// 导出一个函数,用于获取产品列表
|
// 导出一个函数,用于获取产品列表
|
||||||
export const fetchProductList = () => {
|
export const fetchProductList = () => {
|
||||||
// 使用Http.get方法,发送GET请求,获取产品列表
|
// 使用Http.get方法,发送GET请求,获取产品列表
|
||||||
return Http.get('/v1/products/list', {}, { headers: { 'enterprise-id': 1 } });
|
return Http.get('/v1/products/list');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 导出一个函数,用于获取成功案例列表
|
// 导出一个函数,用于获取成功案例列表
|
||||||
@ -109,5 +107,55 @@ export const fetchSuccessCaseList = () => {
|
|||||||
|
|
||||||
// 试用产品
|
// 试用产品
|
||||||
export const trialProduct = (id: number) => {
|
export const trialProduct = (id: number) => {
|
||||||
return Http.post(`/v1/products/${id}/try`, {}, { headers: { 'enterprise-id': 1 } });
|
return Http.post(`/v1/products/${id}/try`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 修改企业名称
|
||||||
|
export const updateEnterpriseName = (data: any) => {
|
||||||
|
return Http.patch(`/v1/enterprises/name`, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送修改手机号验证码
|
||||||
|
export const sendUpdateMobileCaptcha = (data: any) => {
|
||||||
|
return Http.post(`/v1/sms/update-mobile-captcha`, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 修改绑定的手机号
|
||||||
|
export const updateMobile = (data: any) => {
|
||||||
|
return Http.post(`/v1/me/mobile`, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 修改我的信息
|
||||||
|
export const updateMyInfo = (data: any) => {
|
||||||
|
return Http.put(`/v1/me`, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取企业账号分页
|
||||||
|
export const fetchSubAccountPage = (params: any) => {
|
||||||
|
return Http.get(`/v1/enterprises/users`, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取企业账号分页
|
||||||
|
export const fetchImageUploadFile = (params: any) => {
|
||||||
|
return Http.get(`/v1/oss/image-pre-signed-url`, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 移除企业子账号
|
||||||
|
export const removeEnterpriseAccount = (userId: number) => {
|
||||||
|
return Http.delete(`/v1/enterprises/users/${userId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取企业邀请码
|
||||||
|
export const getEnterpriseInviteCode = () => {
|
||||||
|
return Http.get(`/v1/enterprises/invite-code`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据邀请码获取企业信息
|
||||||
|
export const getEnterpriseByInviteCode = (inviteCode: string) => {
|
||||||
|
return Http.get(`/v1/enterprises/by-invite-code`, { invite_code: inviteCode });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据邀请码加入企业
|
||||||
|
export const joinEnterpriseByInviteCode = (inviteCode: string) => {
|
||||||
|
return Http.post(`/v1/enterprises/join`, { invite_code: inviteCode });
|
||||||
};
|
};
|
||||||
|
|||||||
@ -21,6 +21,11 @@ const HttpStatusCode = {
|
|||||||
InternalServerError: 500,
|
InternalServerError: 500,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||||
|
import pinia from '@/stores';
|
||||||
|
const store = useEnterpriseStore(pinia);
|
||||||
|
const enterprise = store.getEnterpriseInfo();
|
||||||
|
|
||||||
//* 导出Request类,可以用来自定义传递配置来创建实例
|
//* 导出Request类,可以用来自定义传递配置来创建实例
|
||||||
export class Request {
|
export class Request {
|
||||||
//* axios 实例
|
//* axios 实例
|
||||||
@ -41,6 +46,15 @@ export class Request {
|
|||||||
(config: AxiosRequestConfig) => {
|
(config: AxiosRequestConfig) => {
|
||||||
const token = localStorage.getItem('accessToken') as string;
|
const token = localStorage.getItem('accessToken') as string;
|
||||||
config.headers!.Authorization = token;
|
config.headers!.Authorization = token;
|
||||||
|
if (token) {
|
||||||
|
config.headers!.Authorization = token;
|
||||||
|
} else {
|
||||||
|
config.headers!.satoken = '123';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enterprise) {
|
||||||
|
config.headers!['enterprise-id'] = enterprise.id;
|
||||||
|
}
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
(err: any) => {
|
(err: any) => {
|
||||||
@ -87,6 +101,10 @@ export class Request {
|
|||||||
public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
||||||
return this.instance.delete(url, config);
|
return this.instance.delete(url, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
|
||||||
|
return this.instance.patch(url, data, config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//* 默认导出Request实例
|
//* 默认导出Request实例
|
||||||
|
|||||||
3
src/assets/warning.svg
Normal file
3
src/assets/warning.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.66675 9.99984C1.66675 5.39746 5.39771 1.6665 10.0001 1.6665C14.6025 1.6665 18.3334 5.39746 18.3334 9.99984C18.3334 14.6022 14.6025 18.3332 10.0001 18.3332C5.39771 18.3332 1.66675 14.6022 1.66675 9.99984ZM9.16675 12.4998V14.1665H10.8334V12.4998H9.16675ZM10.8334 11.6665L10.8334 5.83317L9.16675 5.83317L9.16675 11.6665H10.8334Z" fill="#FFAE00"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 499 B |
@ -10,7 +10,9 @@ const clickExit = () => {
|
|||||||
};
|
};
|
||||||
const getMenus = async () => {
|
const getMenus = async () => {
|
||||||
const res = await fetchMenusTree();
|
const res = await fetchMenusTree();
|
||||||
lists.value = res;
|
if (res.code == 200) {
|
||||||
|
lists.value = res.data;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getMenus();
|
getMenus();
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<div class="flex item-center arco-row-justify-space-between">
|
||||||
<h1 class="title">{{ props.title }}</h1>
|
<h1 class="title">{{ props.title }}</h1>
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
@ -17,6 +20,7 @@ const props = defineProps<{
|
|||||||
border: 1px solid var(--BG-300, rgba(230, 230, 232, 1));
|
border: 1px solid var(--BG-300, rgba(230, 230, 232, 1));
|
||||||
background: var(--BG-white, rgba(255, 255, 255, 1));
|
background: var(--BG-white, rgba(255, 255, 255, 1));
|
||||||
padding: 16px 24px 20px 24px;
|
padding: 16px 24px 20px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
font-family: Alibaba PuHuiTi, serif;
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
@ -24,6 +28,7 @@ const props = defineProps<{
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-bottom: 0px;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
12
src/components/customer-service-modal.vue
Normal file
12
src/components/customer-service-modal.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<Modal title="扫描下面二维码联系客户" v-bind="$attrs">
|
||||||
|
<div class="text-center mt-16px mb-16px">
|
||||||
|
<img width="200" src="@/assets/customer-service.svg" alt="" />
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Modal from '@/components/modal.vue';
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
</style>
|
||||||
58
src/components/delete-modal.vue
Normal file
58
src/components/delete-modal.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal modal-class="delete-modal" body-class="body" cancel-text="返回" ok-text="确定删除" v-bind="$attrs">
|
||||||
|
<h2 class="delete-modal-title flex item-center">
|
||||||
|
<img src="@/assets/warning.svg" alt="" />
|
||||||
|
{{ $attrs.title }}
|
||||||
|
</h2>
|
||||||
|
<slot></slot>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
:deep(.arco-btn-status-danger) {
|
||||||
|
background-color: red !important;
|
||||||
|
width: 1000px !important;
|
||||||
|
}
|
||||||
|
.delete-modal {
|
||||||
|
.arco-modal-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.delete-modal-title {
|
||||||
|
margin-top: 24px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--Text-1, rgba(33, 31, 36, 1));
|
||||||
|
img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-modal-footer {
|
||||||
|
border-top: none;
|
||||||
|
:first-child {
|
||||||
|
border: 1px solid var(--BG-500, rgba(177, 178, 181, 1));
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 7px 20px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
:last-child {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 7px 20px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 16px;
|
||||||
|
border-color: var(--Functional-Danger-6, rgba(246, 75, 49, 1)) !important;
|
||||||
|
background-color: var(--Functional-Danger-6, rgba(246, 75, 49, 1)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.body {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
47
src/components/join-modal.vue
Normal file
47
src/components/join-modal.vue
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<Modal title="加入企业" @ok="handleJoin">
|
||||||
|
<div v-if="enterprise" class="join-body flex item-center">
|
||||||
|
<img src="@/assets/warning.svg" alt="" />
|
||||||
|
{{ `确定加入 “${enterprise.name}”吗?` }}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Modal from '@components/modal.vue';
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { getQueryParam } from '@/utils/helper';
|
||||||
|
|
||||||
|
import { getEnterpriseByInviteCode, joinEnterpriseByInviteCode } from '@/api/all';
|
||||||
|
const enterprise = ref();
|
||||||
|
const inviteCode = ref();
|
||||||
|
|
||||||
|
async function getEnterprise() {
|
||||||
|
inviteCode.value = getQueryParam('invite_code');
|
||||||
|
if (inviteCode.value) {
|
||||||
|
enterprise.value = await getEnterpriseByInviteCode(inviteCode.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleJoin() {
|
||||||
|
await joinEnterpriseByInviteCode(inviteCode.value);
|
||||||
|
AMessage.success('加入成功');
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
getEnterprise();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.join-body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--Text-1, rgba(33, 31, 36, 1));
|
||||||
|
img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 12px;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
38
src/components/modal.vue
Normal file
38
src/components/modal.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal title-align="start" modal-class="modal" body-class="body" v-bind="$attrs" >
|
||||||
|
<slot></slot>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.modal {
|
||||||
|
.arco-modal-header {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.arco-modal-footer {
|
||||||
|
border-top: none;
|
||||||
|
:first-child {
|
||||||
|
border: 1px solid var(--BG-500, rgba(177, 178, 181, 1));
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 7px 20px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
:last-child {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 7px 20px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.body {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,7 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAppStore } from '@/stores';
|
import { useAppStore } from '@/stores';
|
||||||
import { useResponsive } from '@/hooks';
|
import { useResponsive } from '@/hooks';
|
||||||
|
import JoinModal from '@/components/join-modal.vue';
|
||||||
|
import { getQueryParam } from '@/utils/helper';
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
|
||||||
|
const joinEnterpriseVisible = ref(false);
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -27,11 +31,19 @@ const route = useRoute();
|
|||||||
// onMounted(() => {
|
// onMounted(() => {
|
||||||
// showSidebar.value = route.meta.requiresSidebar == true;
|
// showSidebar.value = route.meta.requiresSidebar == true;
|
||||||
// });
|
// });
|
||||||
|
onMounted(() => {
|
||||||
|
checkHasInviteCode();
|
||||||
|
});
|
||||||
const setCollapsed = (val: boolean) => {
|
const setCollapsed = (val: boolean) => {
|
||||||
appStore.updateSettings({ menuCollapse: val });
|
appStore.updateSettings({ menuCollapse: val });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkHasInviteCode = () => {
|
||||||
|
const inviteCode = getQueryParam('invite_code');
|
||||||
|
if (inviteCode) {
|
||||||
|
joinEnterpriseVisible.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
const drawerVisible = ref(false);
|
const drawerVisible = ref(false);
|
||||||
const drawerCancel = () => {
|
const drawerCancel = () => {
|
||||||
drawerVisible.value = false;
|
drawerVisible.value = false;
|
||||||
@ -43,6 +55,7 @@ provide('toggleDrawerMenu', () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-layout :class="['layout', { mobile: appStore.hideMenu }]">
|
<a-layout :class="['layout', { mobile: appStore.hideMenu }]">
|
||||||
|
<JoinModal v-model:visible="joinEnterpriseVisible" />
|
||||||
<div v-if="navbar" class="layout-navbar">
|
<div v-if="navbar" class="layout-navbar">
|
||||||
<base-navbar />
|
<base-navbar />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -50,6 +50,21 @@ const router = createRouter({
|
|||||||
name: 'auth',
|
name: 'auth',
|
||||||
component: () => import('@/views/components/permission/auth.vue'),
|
component: () => import('@/views/components/permission/auth.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/management/person',
|
||||||
|
name: 'person',
|
||||||
|
component: () => import('@/views/components/management/person'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/management/enterprise',
|
||||||
|
name: 'enterprise',
|
||||||
|
component: () => import('@/views/components/management/enterprise'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/management/account',
|
||||||
|
name: 'account',
|
||||||
|
component: () => import('@/views/components/management/account'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
scrollBehavior() {
|
scrollBehavior() {
|
||||||
return { top: 0 };
|
return { top: 0 };
|
||||||
|
|||||||
49
src/stores/modules/enterprise/index.ts
Normal file
49
src/stores/modules/enterprise/index.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
interface EnterpriseInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
update_name_quota: number;
|
||||||
|
used_update_name_count: number;
|
||||||
|
sub_account_quota: number;
|
||||||
|
used_sub_account_count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EnterpriseState {
|
||||||
|
enterpriseInfo: EnterpriseInfo | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useEnterpriseStore = defineStore('enterprise', {
|
||||||
|
state: (): EnterpriseState => ({
|
||||||
|
// todo 暂时写死,登录功能完成后记得重置为null哦
|
||||||
|
enterpriseInfo: {
|
||||||
|
id: 1,
|
||||||
|
name: '企业1',
|
||||||
|
update_name_quota: 2,
|
||||||
|
used_update_name_count: 1,
|
||||||
|
sub_account_quota: 2,
|
||||||
|
used_sub_account_count: 2,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
setEnterpriseInfo(enterpriseInfo: EnterpriseInfo) {
|
||||||
|
this.enterpriseInfo = enterpriseInfo;
|
||||||
|
},
|
||||||
|
setEnterpriseName(name: string) {
|
||||||
|
if (this.enterpriseInfo) {
|
||||||
|
this.enterpriseInfo.name = name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
incUsedUpdateNameCount() {
|
||||||
|
if (this.enterpriseInfo) {
|
||||||
|
this.enterpriseInfo.used_update_name_count++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
incUsedSubAccountCount() {
|
||||||
|
if (this.enterpriseInfo) {
|
||||||
|
this.enterpriseInfo.used_sub_account_count++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getEnterpriseInfo(): EnterpriseInfo | null {
|
||||||
|
return this.enterpriseInfo;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -19,6 +19,13 @@ interface UserState {
|
|||||||
token: string;
|
token: string;
|
||||||
userInfo: UserInfo | null;
|
userInfo: UserInfo | null;
|
||||||
companyInfo: CompanyInfo | null;
|
companyInfo: CompanyInfo | null;
|
||||||
|
isLogin: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserInfo {
|
||||||
|
id: number;
|
||||||
|
mobile: string;
|
||||||
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', {
|
export const useUserStore = defineStore('user', {
|
||||||
@ -26,67 +33,47 @@ export const useUserStore = defineStore('user', {
|
|||||||
token: localStorage.getItem('accessToken') || '',
|
token: localStorage.getItem('accessToken') || '',
|
||||||
userInfo: null,
|
userInfo: null,
|
||||||
companyInfo: null,
|
companyInfo: null,
|
||||||
|
isLogin: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
setToken(token: String) {
|
// 设置 Token
|
||||||
const _token = `Bearer ${token}`;
|
setToken(token: string) {
|
||||||
this.token = _token;
|
this.token = `Bearer ${token}`;
|
||||||
localStorage.setItem('accessToken', _token);
|
localStorage.setItem('accessToken', this.token);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 存储用户信息
|
// 获取 Token
|
||||||
|
getToken() {
|
||||||
|
return this.token;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置用户信息
|
||||||
setUserInfo(userInfo: UserInfo | null) {
|
setUserInfo(userInfo: UserInfo | null) {
|
||||||
this.userInfo = userInfo;
|
this.userInfo = userInfo;
|
||||||
if (userInfo) {
|
|
||||||
localStorage.setItem('userInfo', JSON.stringify(userInfo));
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem('userInfo');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
getUserInfo(): UserInfo | null {
|
getUserInfo(): UserInfo | null {
|
||||||
const userInfoStr = localStorage.getItem('userInfo');
|
return this.userInfo;
|
||||||
if (userInfoStr) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(userInfoStr);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('解析用户信息失败:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 存储公司信息
|
// 设置公司信息
|
||||||
setCompanyInfo(companyInfo: CompanyInfo | null) {
|
setCompanyInfo(companyInfo: CompanyInfo | null) {
|
||||||
this.companyInfo = companyInfo;
|
this.companyInfo = companyInfo;
|
||||||
if (companyInfo) {
|
|
||||||
localStorage.setItem('companyInfo', JSON.stringify(companyInfo));
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem('companyInfo');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取公司信息
|
// 获取公司信息
|
||||||
getCompanyInfo(): CompanyInfo | null {
|
getCompanyInfo(): CompanyInfo | null {
|
||||||
const companyInfoStr = localStorage.getItem('companyInfo');
|
return this.companyInfo;
|
||||||
if (companyInfoStr) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(companyInfoStr);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('解析公司信息失败:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除 token
|
// 登录状态
|
||||||
deleteToken() {
|
setIsLogin(isLogin: boolean) {
|
||||||
this.token = '';
|
this.isLogin = isLogin;
|
||||||
localStorage.removeItem('accessToken');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getIsLogin(): boolean {
|
||||||
|
return this.isLogin;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
5
src/utils/helper.ts
Normal file
5
src/utils/helper.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export function getQueryParam(param: string): string | null {
|
||||||
|
const search = window.location.search || window.location.hash.split('?')[1] || '';
|
||||||
|
const params = new URLSearchParams(search);
|
||||||
|
return params.get(param);
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">基于行业内内容提取的高频词汇。</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -84,12 +84,17 @@ const getIndustryTerms = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value !== 0) {
|
if (selectedSubCategory.value !== 0) {
|
||||||
params['industry_id'] = selectedSubCategory.value;
|
params['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
const res = await fetchindustryTerms(params);
|
const res = await fetchindustryTerms(params);
|
||||||
|
if (res.code === 200) {
|
||||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||||
tagRows.value = processTagData(res.slice(0, 70));
|
tagRows.value = processTagData(res.data.slice(0, 70));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 标签数据(按行分组)
|
// 标签数据(按行分组)
|
||||||
|
|||||||
@ -12,33 +12,24 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">基于社交内容平台的行业数据,分析用户关注的热门话题与趋势。</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-table :data="dataList">
|
<a-table :columns="columns" :data="dataList" :filter-icon-align-left="alignLeft" @change="handleChange">
|
||||||
<template #columns>
|
<template #rank="{ record }">
|
||||||
<a-table-column title="排名" data-index="rank">
|
|
||||||
<template #cell="{ record }">
|
|
||||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||||
<span v-else>{{ record.rank }}</span>
|
<span v-else>{{ record.rank }}</span>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
<template #keywords="{ record }">
|
||||||
<a-table-column title="话题名称" data-index="name" />
|
|
||||||
<a-table-column title="关键词" data-index="keywords">
|
|
||||||
<template #cell="{ record }">
|
|
||||||
<a-tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
|
<a-tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
<template #hot="{ record }">
|
||||||
<a-table-column title="热度" data-index="heatLevel">
|
|
||||||
<template #cell="{ record }">
|
|
||||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
<template #sentiment="{ record }">
|
||||||
<a-table-column title="情感" data-index="sentiment">
|
|
||||||
<template #cell="{ record }">
|
|
||||||
<img
|
<img
|
||||||
v-if="record.felling == '2'"
|
v-if="record.felling == '2'"
|
||||||
src="@/assets/img/hottranslation/good.png"
|
src="@/assets/img/hottranslation/good.png"
|
||||||
@ -55,19 +46,12 @@
|
|||||||
style="width: 16px; height: 16px"
|
style="width: 16px; height: 16px"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
|
||||||
<a-table-column title="操作" data-index="optional">
|
<template #optional="{ record }">
|
||||||
<template #cell="{ record }">
|
|
||||||
<a-button type="outline" @click="gotoDetail(record)">详情</a-button>
|
<a-button type="outline" @click="gotoDetail(record)">详情</a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
|
||||||
</template>
|
|
||||||
<template #rank="{ record }">
|
|
||||||
<a-tag color="blue" v-if="record.rank == 1">1</a-tag>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-space>
|
</a-space>
|
||||||
<!-- modal -->
|
|
||||||
<a-modal :visible="visible" @ok="handleOk" @cancel="handleCancel" unmountOnClose>
|
<a-modal :visible="visible" @ok="handleOk" @cancel="handleCancel" unmountOnClose>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span style="text-align: left; width: 100%">行业热门话题洞察</span>
|
<span style="text-align: left; width: 100%">行业热门话题洞察</span>
|
||||||
@ -135,12 +119,63 @@ import star5 from '@/assets/img/hottranslation/star-fill5.png';
|
|||||||
import top1 from '@/assets/img/captcha/top1.svg';
|
import top1 from '@/assets/img/captcha/top1.svg';
|
||||||
import top2 from '@/assets/img/captcha/top2.svg';
|
import top2 from '@/assets/img/captcha/top2.svg';
|
||||||
import top3 from '@/assets/img/captcha/top3.svg';
|
import top3 from '@/assets/img/captcha/top3.svg';
|
||||||
|
import { IconQuestionCircle, IconArrowUp, IconArrowDown } from '@arco-design/web-vue/es/icon';
|
||||||
|
// 新增排序状态和函数
|
||||||
|
const heatSortDirection = ref('desc'); // 默认降序排列
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '排名',
|
||||||
|
dataIndex: 'rank',
|
||||||
|
slotName: 'rank',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '话题名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '关键词',
|
||||||
|
dataIndex: 'keywords',
|
||||||
|
slotName: 'keywords',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '热度指数',
|
||||||
|
dataIndex: 'hot',
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
slotName: 'hot',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '情感倾向',
|
||||||
|
dataIndex: 'sentiment',
|
||||||
|
slotName: 'sentiment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'optional',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// 切换排序方向
|
||||||
|
const toggleHeatSort = () => {
|
||||||
|
heatSortDirection.value = heatSortDirection.value === 'asc' ? 'desc' : 'asc';
|
||||||
|
sortDataByHeat();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 实际排序逻辑
|
||||||
|
const sortDataByHeat = () => {
|
||||||
|
dataList.value.sort((a, b) => {
|
||||||
|
return heatSortDirection.value === 'asc' ? a.hot - b.hot : b.hot - a.hot;
|
||||||
|
});
|
||||||
|
// 排序后更新排名
|
||||||
|
dataList.value.forEach((item, index) => {
|
||||||
|
item.rank = index + 1;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const starImages = [star1, star2, star3, star4, star5];
|
const starImages = [star1, star2, star3, star4, star5];
|
||||||
const topImages = [top1, top2, top3];
|
const topImages = [top1, top2, top3];
|
||||||
// 行业大类
|
// 行业大类
|
||||||
const industriesTree = ref([]);
|
const industriesTree = ref([]);
|
||||||
|
|
||||||
// 行业热门话题洞察
|
// 行业热门话题洞察
|
||||||
const dataList = ref([]);
|
const dataList = ref([]);
|
||||||
// 显示详情
|
// 显示详情
|
||||||
@ -174,26 +209,36 @@ const getIndustriesTree = async () => {
|
|||||||
getIndustryTopics();
|
getIndustryTopics();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSort = () => {
|
||||||
|
console.log('table change');
|
||||||
|
};
|
||||||
|
|
||||||
// 行业热门话题
|
// 行业热门话题
|
||||||
const getIndustryTopics = async () => {
|
const getIndustryTopics = async () => {
|
||||||
let parms = {
|
let parms = {
|
||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value != undefined && selectedSubCategory.value != 0) {
|
||||||
parms['industry_id'] = selectedSubCategory.value;
|
parms['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
const res = await fetchIndustryTopics(parms);
|
const res = await fetchIndustryTopics(parms);
|
||||||
dataList.value = res;
|
if (res.code == 200) {
|
||||||
|
dataList.value = res.data;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 详情
|
// 详情
|
||||||
const gotoDetail = async (record) => {
|
const gotoDetail = async (record) => {
|
||||||
console.log(record);
|
console.log(record);
|
||||||
const res = await fetchIndustryTopicDetail(record.id);
|
const res = await fetchIndustryTopicDetail(record.id);
|
||||||
console.log(res);
|
if (res.code == 200) {
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
topicInfo.value = res;
|
topicInfo.value = res.data;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 弹窗的取消
|
// 弹窗的取消
|
||||||
@ -230,4 +275,14 @@ const handleOk = () => {
|
|||||||
color: #737478 !important;
|
color: #737478 !important;
|
||||||
margin-left: -5px;
|
margin-left: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.arco-icon) {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮悬停效果 */
|
||||||
|
:deep(.arco-btn-text:not(.arco-btn-disabled):hover) {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">基于该行业中近期提及频次高、用户互动活跃的品牌内容,筛选出关注度较高的代表性品牌。</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -70,7 +70,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">
|
||||||
|
基于情绪分析与敏感词识别,对行业内容中的负面或争议性话题进行监测,辅助判断舆情风险动态。
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -116,6 +118,12 @@ const getFocusBrandsList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
params['industry_id'] = selectedSubCategory.value;
|
params['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
@ -129,6 +137,12 @@ const getEventDynamicsList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
params['industry_id'] = selectedSubCategory.value;
|
params['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">基于该行业用户内容中提及频率较高的关键词,按热度进行排序,反映近期关注焦点。</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -92,7 +92,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">
|
||||||
|
对该行业下用户内容进行情绪分析,按情绪类别统计占比,提取占比最高者作为行业情绪代表。
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -153,7 +155,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">指当前周期中首次出现,或相较上一周期词频显著增长的关键词,反映近期出现的新关注点。</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -306,6 +308,12 @@ const getIndustryEmotions = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
params['industry_id'] = selectedSubCategory.value;
|
params['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
@ -356,6 +364,12 @@ const getKeywordTrendsList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
params['industry_id'] = selectedSubCategory.value;
|
params['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
@ -380,12 +394,20 @@ const getNewKeywordList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value != 0 && selectedSubCategory.value != undefined) {
|
||||||
params['industry_id'] = selectedSubCategory.value;
|
params['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
const res = await fetchNewKeywordList(params);
|
const res = await fetchNewKeywordList(params);
|
||||||
|
if (res.code == 200) {
|
||||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||||
keywordList.value = res;
|
keywordList.value = res.data;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawChart = () => {
|
const drawChart = () => {
|
||||||
|
|||||||
@ -5,10 +5,9 @@
|
|||||||
direction="vertical"
|
direction="vertical"
|
||||||
style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0; color: #737478; font-size: 14px"
|
style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0; color: #737478; font-size: 14px"
|
||||||
>
|
>
|
||||||
<a-space align="center">
|
<a-space align="start" style="width: 100%; margin-top: 20px; align-items: flex-start">
|
||||||
<!-- 行业选择 -->
|
<span style="width: 60px; flex-shrink: 0; line-height: 28px">行业大类</span>
|
||||||
<a-space align="center">
|
<div style="display: flex; flex-wrap: wrap; gap: 8px; width: 100%; align-items: flex-start">
|
||||||
<span>行业大类</span>
|
|
||||||
<a-tag
|
<a-tag
|
||||||
size="Medium"
|
size="Medium"
|
||||||
v-for="item in industriesTree"
|
v-for="item in industriesTree"
|
||||||
@ -24,12 +23,12 @@
|
|||||||
"
|
"
|
||||||
>{{ item.name }}</a-tag
|
>{{ item.name }}</a-tag
|
||||||
>
|
>
|
||||||
|
</div>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-space>
|
|
||||||
<a-space align="center" style="margin-left: 'auto'; margin-top: 20px">
|
|
||||||
<!-- 二级类目 -->
|
<!-- 二级类目 -->
|
||||||
<a-space align="center">
|
<a-space align="start" style="width: 100%; margin-top: 20px; align-items: flex-start">
|
||||||
<span>二级类目</span>
|
<span style="width: 60px; flex-shrink: 0; line-height: 28px">二级类目</span>
|
||||||
|
<div style="display: flex; flex-wrap: wrap; gap: 8px; width: 100%; align-items: flex-start">
|
||||||
<a-tag
|
<a-tag
|
||||||
size="Medium"
|
size="Medium"
|
||||||
v-for="item in subCategories"
|
v-for="item in subCategories"
|
||||||
@ -45,12 +44,12 @@
|
|||||||
"
|
"
|
||||||
>{{ item.name }}</a-tag
|
>{{ item.name }}</a-tag
|
||||||
>
|
>
|
||||||
|
</div>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-space>
|
<!-- </a-space> -->
|
||||||
<a-space align="center" style="margin-left: 'auto'; margin-top: 20px">
|
<a-space align="start" style="width: 100%; margin-top: 20px; align-items: flex-start">
|
||||||
<!-- 时间筛选 -->
|
<span style="width: 60px; flex-shrink: 0; line-height: 28px">时间筛选</span>
|
||||||
<a-space align="center">
|
<div style="display: flex; flex-wrap: wrap; gap: 8px; width: 100%; align-items: flex-start">
|
||||||
<span>时间筛选</span>
|
|
||||||
<a-tag
|
<a-tag
|
||||||
size="Medium"
|
size="Medium"
|
||||||
v-for="item in timePeriods"
|
v-for="item in timePeriods"
|
||||||
@ -66,7 +65,7 @@
|
|||||||
"
|
"
|
||||||
>{{ item.label }}
|
>{{ item.label }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a-space>
|
</div>
|
||||||
</a-space>
|
</a-space>
|
||||||
<!-- 搜索区域 -->
|
<!-- 搜索区域 -->
|
||||||
<a-space style="margin-left: 'auto'; margin-top: 20px">
|
<a-space style="margin-left: 'auto'; margin-top: 20px">
|
||||||
@ -161,11 +160,14 @@ onMounted(() => {
|
|||||||
// 获取行业大类数据
|
// 获取行业大类数据
|
||||||
const getIndustriesTree = async () => {
|
const getIndustriesTree = async () => {
|
||||||
const res = await fetchIndustriesTree();
|
const res = await fetchIndustriesTree();
|
||||||
industriesTree.value = res;
|
if (res.code == 200) {
|
||||||
selectedIndustry.value = res[0].id;
|
let data = res['data'];
|
||||||
|
industriesTree.value = data;
|
||||||
|
selectedIndustry.value = data[0].id;
|
||||||
selectedSubCategory.value = 0;
|
selectedSubCategory.value = 0;
|
||||||
subCategories.value = [...industriesTree.value[0].children];
|
subCategories.value = [...industriesTree.value[0].children];
|
||||||
subCategories.value.unshift({ id: 0, name: '全部' });
|
subCategories.value.unshift({ id: 0, name: '全部' });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'search'): void;
|
(e: 'search'): void;
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
<p style="margin: 0">基于用户内容中的情绪分析与表达模式,提取反复出现的负面倾向主题,反映典型使用痛点。</p>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -140,6 +140,12 @@ const getUserPainPointsList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
params['industry_id'] = selectedSubCategory.value;
|
params['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,6 +173,12 @@ const getAgeDistributionsList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
parms['industry_id'] = selectedSubCategory.value;
|
parms['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
@ -189,6 +195,12 @@ const getGeoDistributionsList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
parms['industry_id'] = selectedSubCategory.value;
|
parms['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
@ -202,6 +214,12 @@ const getGenderDistributionsList = async () => {
|
|||||||
industry_id: selectedIndustry.value,
|
industry_id: selectedIndustry.value,
|
||||||
time_dimension: selectedTimePeriod.value,
|
time_dimension: selectedTimePeriod.value,
|
||||||
};
|
};
|
||||||
|
if (selectedIndustry.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedSubCategory.value == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedSubCategory.value != 0) {
|
if (selectedSubCategory.value != 0) {
|
||||||
parms['industry_id'] = selectedSubCategory.value;
|
parms['industry_id'] = selectedSubCategory.value;
|
||||||
}
|
}
|
||||||
|
|||||||
274
src/views/components/management/account/index.vue
Normal file
274
src/views/components/management/account/index.vue
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
<template>
|
||||||
|
<Container title="账号信息" class="container mt-24px">
|
||||||
|
<template #header>
|
||||||
|
<a-button type="outline" class="add-account-button" @click="handleAddAccount">添加子账号</a-button>
|
||||||
|
</template>
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
:pagination="pagination"
|
||||||
|
class="mt-16px"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
>
|
||||||
|
<template #mobile="{ record }">
|
||||||
|
<div class="flex item-center pt-13px pb-13px">
|
||||||
|
<span class="mr-4px">{{ record.mobile }}</span>
|
||||||
|
<a-tag v-if="record.type === 0" class="primary-account">主账号</a-tag>
|
||||||
|
<a-tag v-else class="sub-account">子账号</a-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #action="{ record }">
|
||||||
|
<a-button
|
||||||
|
v-if="record.type !== 0"
|
||||||
|
class="delete-button"
|
||||||
|
size="mini"
|
||||||
|
type="outline"
|
||||||
|
status="danger"
|
||||||
|
@click="openDeleteModal(record)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<Modal v-model:visible="addAccountVisible" width="480px" title="添加子账号" :okText="okText" @ok="handleOk">
|
||||||
|
<div v-if="canAddAccount" class="add-account-container">
|
||||||
|
<h2 class="add-account-title">生成企业专属链接,成员通过访问即可注册并加入企业账号。</h2>
|
||||||
|
<p class="add-account-subtitle">子账号可独立登录,权限继承主账号配置。</p>
|
||||||
|
<div class="add-account-body">
|
||||||
|
<p>用该链接加入企业吧!</p>
|
||||||
|
<p>{{ inviteUrl }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="add-account-container">
|
||||||
|
<h2 class="cannot-add-account-title flex item-center">
|
||||||
|
<img src="@/assets/warning.svg" alt="">
|
||||||
|
当前可用子账号数为0。
|
||||||
|
</h2>
|
||||||
|
<p class="cannot-add-account-subtitle">如需添加更多子账号,您可联系销售人员进行购买和权限扩展。</p>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<CustomerServiceModal v-model:visible="customerServiceVisible" />
|
||||||
|
<DeleteModal v-model:visible="deleteVisible" :title="deleteTitle" @ok="handleDelete">
|
||||||
|
<p class="delete-modal-content">删除后,该账号将无法登录您的企业。</p>
|
||||||
|
</DeleteModal>
|
||||||
|
</Container>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Container from '@/components/container.vue';
|
||||||
|
import { ref, onMounted, reactive, computed } from 'vue';
|
||||||
|
import { fetchSubAccountPage, removeEnterpriseAccount, getEnterpriseInviteCode } from '@/api/all';
|
||||||
|
import Modal from '@/components/modal.vue';
|
||||||
|
import DeleteModal from '@/components/delete-modal.vue';
|
||||||
|
import CustomerServiceModal from '@/components/customer-service-modal.vue';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||||
|
|
||||||
|
const store = useEnterpriseStore();
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
slotName: 'mobile',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'action',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const data = ref([]);
|
||||||
|
const pagination = reactive({
|
||||||
|
total: 0,
|
||||||
|
showPageSize: true,
|
||||||
|
showTotal: true,
|
||||||
|
defaultCurrent: 1,
|
||||||
|
defaultPageSize: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const params = reactive({
|
||||||
|
page_size: 10,
|
||||||
|
page: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const inviteUrl = ref('');
|
||||||
|
|
||||||
|
const addAccountVisible = ref(false);
|
||||||
|
const deleteVisible = ref(false);
|
||||||
|
const deleteTitle = ref('');
|
||||||
|
|
||||||
|
const enterpriseInfo = store.getEnterpriseInfo();
|
||||||
|
|
||||||
|
const okText = computed(() => {
|
||||||
|
if (!canAddAccount.value) {
|
||||||
|
return '联系客服';
|
||||||
|
}
|
||||||
|
return '复制邀请链接';
|
||||||
|
});
|
||||||
|
const customerServiceVisible = ref(false);
|
||||||
|
const canAddAccount = computed(() => {
|
||||||
|
return enterpriseInfo.sub_account_quota > enterpriseInfo.used_sub_account_count;
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentSelectAccount = ref();
|
||||||
|
|
||||||
|
const { copy, copied, isSupported } = useClipboard({ source: inviteUrl });
|
||||||
|
|
||||||
|
function handlePageChange(current: number) {
|
||||||
|
params.page = current;
|
||||||
|
getSubAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePageSizeChange(pageSize: number) {
|
||||||
|
params.page_size = pageSize;
|
||||||
|
getSubAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSubAccount() {
|
||||||
|
const res = await fetchSubAccountPage(params);
|
||||||
|
pagination.total = res.total;
|
||||||
|
data.value = res.data;
|
||||||
|
}
|
||||||
|
async function handleAddAccount() {
|
||||||
|
if (canAddAccount.value) {
|
||||||
|
const res = await getEnterpriseInviteCode();
|
||||||
|
const port = window.location.port === '' ? '' : ':' + window.location.port;
|
||||||
|
const domain = window.location.protocol + '//' + window.location.hostname + port;
|
||||||
|
inviteUrl.value = domain + '?invite_code=' + res.invite_code;
|
||||||
|
}
|
||||||
|
addAccountVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOk() {
|
||||||
|
if (!canAddAccount.value) {
|
||||||
|
customerServiceVisible.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isSupported) {
|
||||||
|
AMessage.error('您的浏览器不支持复制,请手动复制!');
|
||||||
|
}
|
||||||
|
copy(inviteUrl.value);
|
||||||
|
if (!copied) {
|
||||||
|
AMessage.error('复制失败,请手动复制!');
|
||||||
|
}
|
||||||
|
AMessage.success('复制成功!');
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDeleteModal(record: { id: number; mobile: string }) {
|
||||||
|
currentSelectAccount.value = record;
|
||||||
|
deleteTitle.value = `确认删除“${record.mobile}”子账号吗?`;
|
||||||
|
deleteVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete() {
|
||||||
|
await removeEnterpriseAccount(currentSelectAccount.value.id);
|
||||||
|
AMessage.success('移除成功!');
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getSubAccount();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.primary-account {
|
||||||
|
border-radius: 2px;
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-left: 8px;
|
||||||
|
background: var(--Brand-Brand-1, rgba(240, 237, 255, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: var(--Brand-Brand-6, rgba(109, 76, 254, 1));
|
||||||
|
}
|
||||||
|
.sub-account {
|
||||||
|
border-radius: 2px;
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-left: 8px;
|
||||||
|
background: var(--Functional-Warning-1, rgba(255, 245, 222, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--Functional-Warning-6, rgba(255, 174, 0, 1));
|
||||||
|
}
|
||||||
|
.delete-button {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 12px;
|
||||||
|
border: 1px solid var(--Functional-Danger-6, rgba(246, 75, 49, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: var(--Functional-Danger-6, rgba(246, 75, 49, 1));
|
||||||
|
}
|
||||||
|
.add-account-button {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 5px 16px;
|
||||||
|
border: 1px solid var(--Brand-Brand-6, rgba(109, 76, 254, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--Brand-Brand-6, rgba(109, 76, 254, 1));
|
||||||
|
}
|
||||||
|
.add-account-container {
|
||||||
|
margin-top: 13px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
.add-account-title {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--Text-1, rgba(33, 31, 36, 1));
|
||||||
|
}
|
||||||
|
.add-account-subtitle {
|
||||||
|
margin: 4px 0 0 0;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||||
|
}
|
||||||
|
.add-account-body {
|
||||||
|
margin-top: 16px;
|
||||||
|
width: 432px;
|
||||||
|
height: 84px;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--BG-200, rgba(242, 243, 245, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||||
|
p {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cannot-add-account-title {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--Text-1, rgba(33, 31, 36, 1));
|
||||||
|
img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cannot-add-account-subtitle {
|
||||||
|
margin: 16px 0 0 0;
|
||||||
|
padding-left: 32px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.delete-modal-content {
|
||||||
|
margin-left: 34px;
|
||||||
|
margin-top: 16px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
155
src/views/components/management/enterprise/index.vue
Normal file
155
src/views/components/management/enterprise/index.vue
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<Container title="企业信息" class="container mt-24px">
|
||||||
|
<a-table :columns="columns" :data="data" :pagination="false" class="mt-16px">
|
||||||
|
<template #info="{ record }">
|
||||||
|
{{ record.name }}
|
||||||
|
</template>
|
||||||
|
<template #action>
|
||||||
|
<a-button class="edit-button" size="mini" type="outline" @click="handleUpdate">修改</a-button>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<Modal v-model:visible="infoVisible" width="480px" title="修改企业名称" :okText="okText" @ok="handleOk">
|
||||||
|
<p class="tips">
|
||||||
|
企业名称只能修改2次,请谨慎操作。<span
|
||||||
|
>(剩余{{ enterpriseInfo.update_name_quota - enterpriseInfo.used_update_name_count }}次)
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<a-form
|
||||||
|
:model="form"
|
||||||
|
class="form"
|
||||||
|
:label-col-props="{ span: 6, offset: 0 }"
|
||||||
|
:wrapper-col-props="{ span: 18, offset: 0 }"
|
||||||
|
label-align="left"
|
||||||
|
>
|
||||||
|
<a-form-item required field="name" label="新企业名称">
|
||||||
|
<a-input v-model.trim="form.name" size="small" :disabled="!canUpdate" placeholder="请输入新企业名称" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</Modal>
|
||||||
|
<CustomerServiceModal v-model:visible="customerServiceVisible" />
|
||||||
|
</Container>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Container from '@/components/container.vue';
|
||||||
|
import Modal from '@/components/modal.vue';
|
||||||
|
import { ref, reactive, computed } from 'vue';
|
||||||
|
import CustomerServiceModal from '@/components/customer-service-modal.vue';
|
||||||
|
import { updateEnterpriseName } from '@/api/all';
|
||||||
|
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||||
|
|
||||||
|
const store = useEnterpriseStore();
|
||||||
|
const form = reactive({
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const enterpriseInfo = store.getEnterpriseInfo();
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '企业名称',
|
||||||
|
slotName: 'info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'action',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const data = ref([enterpriseInfo]);
|
||||||
|
|
||||||
|
const infoVisible = ref(false);
|
||||||
|
const customerServiceVisible = ref(false);
|
||||||
|
|
||||||
|
const canUpdate = computed(() => {
|
||||||
|
return enterpriseInfo.update_name_quota > enterpriseInfo.used_update_name_count;
|
||||||
|
});
|
||||||
|
|
||||||
|
const okText = computed(() => {
|
||||||
|
if (!canUpdate.value) {
|
||||||
|
return '联系客服';
|
||||||
|
}
|
||||||
|
return '确定';
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleUpdate() {
|
||||||
|
if (!canUpdate.value) {
|
||||||
|
form.name = enterpriseInfo.name;
|
||||||
|
}
|
||||||
|
infoVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleOk() {
|
||||||
|
if (!canUpdate.value) {
|
||||||
|
customerServiceVisible.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await updateEnterpriseName({ name: form.name });
|
||||||
|
store.setEnterpriseName(form.name);
|
||||||
|
store.incUsedUpdateNameCount();
|
||||||
|
AMessage.success('修改成功!');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.tips {
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: var(--BG-100, rgba(247, 248, 250, 1));
|
||||||
|
border: 1px solid var(--BG-300, rgba(230, 230, 232, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
span {
|
||||||
|
color: var(--Functional-Danger-6, rgba(246, 75, 49, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
margin-top: 20px;
|
||||||
|
:deep(.arco-row) {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.arco-form-item-label) {
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
:deep(.arco-input-wrapper) {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid var(--BG-400, rgba(215, 215, 217, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
input::placeholder {
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--Text-4, rgba(147, 148, 153, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.arco-input-disabled) {
|
||||||
|
background: var(--BG-200, rgba(242, 243, 245, 1));
|
||||||
|
border: 1px solid var(--BG-400, rgba(215, 215, 217, 1));
|
||||||
|
.arco-input:disabled {
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.arco-input-focus) {
|
||||||
|
border: 1px solid var(--Brand-Brand-6, rgba(109, 76, 254, 1));
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(109, 76, 254, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.edit-button {
|
||||||
|
margin: 12px 0;
|
||||||
|
border: 1px solid rgba(109, 76, 254, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 12px;
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(109, 76, 254, 1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
305
src/views/components/management/person/index.vue
Normal file
305
src/views/components/management/person/index.vue
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
<template>
|
||||||
|
<Container title="个人信息" class="container mt-24px">
|
||||||
|
<a-table :columns="columns" :data="data" :pagination="false" class="mt-16px">
|
||||||
|
<template #info="{ record }">
|
||||||
|
<div class="pt-3px pb-3px">
|
||||||
|
<a-avatar :image-url="record.head_image" :size="32" />
|
||||||
|
{{ record.name }}
|
||||||
|
<icon-edit size="13" class="ml-8px" @click="openEditInfoModal" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #mobile="{ record }">
|
||||||
|
{{ record.mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') }}
|
||||||
|
<icon-edit size="13" class="ml-8px" @click="openEditMobileModal" />
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<Modal v-model:visible="infoVisible" title="修改用户信息" @ok="handleSubmitUserInfo">
|
||||||
|
<a-form
|
||||||
|
class="form"
|
||||||
|
:model="userInfoForm"
|
||||||
|
:label-col-props="{ span: 3, offset: 0 }"
|
||||||
|
:wrapper-col-props="{ span: 21, offset: 0 }"
|
||||||
|
>
|
||||||
|
<a-form-item field="head_image" label="头像">
|
||||||
|
<div class="flex item-center">
|
||||||
|
<a-avatar :image-url="userInfoForm.file_url" :size="48" />
|
||||||
|
<span class="upload-button" @click="triggerFileInput">
|
||||||
|
<input
|
||||||
|
ref="uploadInputRef"
|
||||||
|
accept="image/*"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
@change="handleFileChange"
|
||||||
|
/>
|
||||||
|
<a-button><icon-upload />上传新头像</a-button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="name" label="昵称">
|
||||||
|
<a-input v-model.trim="userInfoForm.name" placeholder="请输入昵称" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</Modal>
|
||||||
|
<Modal v-model:visible="imageVisible" title="头像裁剪">
|
||||||
|
<VueCropper></VueCropper>
|
||||||
|
</Modal>
|
||||||
|
<Modal v-model:visible="mobileVisible" title="修改手机号" @ok="handleUpdateMobile">
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
class="form"
|
||||||
|
:rules="formRules"
|
||||||
|
:label-col-props="{ span: 5, offset: 0 }"
|
||||||
|
:wrapper-col-props="{ span: 19, offset: 0 }"
|
||||||
|
label-align="left"
|
||||||
|
>
|
||||||
|
<a-form-item required field="mobile" label="新手机号">
|
||||||
|
<a-input v-model.trim="form.mobile" size="small" placeholder="请输入新的手机号" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item required field="captcha" label="获取验证码">
|
||||||
|
<a-input v-model.trim="form.captcha" size="small" placeholder="请输入验证码">
|
||||||
|
<template #suffix>
|
||||||
|
<span v-if="countdown <= 0" @click="sendCaptcha">发送验证码</span>
|
||||||
|
<span v-else>{{ countdown }}s</span>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<PuzzleVerification
|
||||||
|
:show="verificationVisible"
|
||||||
|
@submit="handleVerificationSubmit"
|
||||||
|
@cancel="verificationVisible = false"
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</Container>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Container from '@/components/container.vue';
|
||||||
|
import Modal from '@/components/modal.vue';
|
||||||
|
import PuzzleVerification from '@/views/components/login/PuzzleVerification.vue';
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import 'vue-cropper/dist/index.css';
|
||||||
|
import { VueCropper } from 'vue-cropper';
|
||||||
|
import { sendUpdateMobileCaptcha, updateMobile, fetchImageUploadFile, updateMyInfo } from '@/api/all';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useUserStore } from '@/stores';
|
||||||
|
|
||||||
|
const store = useUserStore();
|
||||||
|
const userInfo = store.getUserInfo();
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '用户信息',
|
||||||
|
slotName: 'info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
slotName: 'mobile',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const data = reactive([userInfo]);
|
||||||
|
const infoVisible = ref(false);
|
||||||
|
const imageVisible = ref(false);
|
||||||
|
const mobileVisible = ref(false);
|
||||||
|
const verificationVisible = ref(false);
|
||||||
|
const timer = ref();
|
||||||
|
const countdown = ref(0);
|
||||||
|
const formRef = ref();
|
||||||
|
const isSendCaptcha = ref(false);
|
||||||
|
const uploadInputRef = ref();
|
||||||
|
|
||||||
|
// 表单校验规则
|
||||||
|
const formRules = {
|
||||||
|
mobile: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请填写手机号',
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (value: string, callback: (error?: string) => void) => {
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(value)) {
|
||||||
|
callback('手机号格式不正确');
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
captcha: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请填写验证码',
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (value: string, callback: (error?: string) => void) => {
|
||||||
|
if (!/^\d{6}$/.test(value)) {
|
||||||
|
callback('验证码必须是6位数字');
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
mobile: '',
|
||||||
|
captcha: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const userInfoForm = reactive({
|
||||||
|
name: '',
|
||||||
|
head_image: '',
|
||||||
|
file_url: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
function triggerFileInput() {
|
||||||
|
uploadInputRef.value.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditInfoModal() {
|
||||||
|
infoVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleFileChange(event: Event) {
|
||||||
|
const target = event.target as HTMLInputElement;
|
||||||
|
const file = target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
const fileExtension = getFileExtension(file.name);
|
||||||
|
const res = await fetchImageUploadFile({
|
||||||
|
suffix: fileExtension,
|
||||||
|
});
|
||||||
|
const blob = new Blob([file], { type: file.type });
|
||||||
|
await axios.put(res.upload_url, blob, {
|
||||||
|
headers: { 'Content-Type': file.type },
|
||||||
|
});
|
||||||
|
userInfoForm.head_image = res.file_name;
|
||||||
|
userInfoForm.file_url = res.file_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function openEditImageModal() {
|
||||||
|
imageVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditMobileModal() {
|
||||||
|
mobileVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmitUserInfo() {
|
||||||
|
await updateMyInfo(userInfoForm);
|
||||||
|
store.setUserName(userInfoForm.name);
|
||||||
|
store.setUserHeadImage(userInfoForm.file_url);
|
||||||
|
AMessage.success('修改成功!');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendCaptcha() {
|
||||||
|
try {
|
||||||
|
const result = await formRef.value.validateField('mobile');
|
||||||
|
if (result === true || result === undefined) {
|
||||||
|
verificationVisible.value = true;
|
||||||
|
isSendCaptcha.value = true;
|
||||||
|
}
|
||||||
|
AMessage.error('请填写正确的手机号!');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('手机号验证失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginCountdown() {
|
||||||
|
timer.value = setInterval(() => {
|
||||||
|
countdown.value--;
|
||||||
|
if (countdown.value <= 0) {
|
||||||
|
clearInterval(timer.value);
|
||||||
|
timer.value = null;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleVerificationSubmit() {
|
||||||
|
await sendUpdateMobileCaptcha({ mobile: form.mobile });
|
||||||
|
AMessage.success('发送成功');
|
||||||
|
verificationVisible.value = false;
|
||||||
|
countdown.value = 60;
|
||||||
|
beginCountdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUpdateMobile() {
|
||||||
|
if (!isSendCaptcha.value) {
|
||||||
|
AMessage.error('请先获取验证码!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const res = await formRef.value.validate();
|
||||||
|
if (res === true || res === undefined) {
|
||||||
|
await updateMobile(form);
|
||||||
|
store.setUserMobile(form.mobile);
|
||||||
|
AMessage.success('修改成功!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileExtension(filename: string): string {
|
||||||
|
const match = filename.match(/\.([^.]+)$/);
|
||||||
|
return match ? match[1].toLowerCase() : '';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="less">
|
||||||
|
.form {
|
||||||
|
margin-top: 13px;
|
||||||
|
:deep(.arco-row) {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.arco-form-item-label) {
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
:deep(.arco-input-wrapper) {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid var(--BG-400, rgba(215, 215, 217, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
input::placeholder {
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--Text-4, rgba(147, 148, 153, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.arco-input-disabled) {
|
||||||
|
background: var(--BG-200, rgba(242, 243, 245, 1));
|
||||||
|
border: 1px solid var(--BG-400, rgba(215, 215, 217, 1));
|
||||||
|
.arco-input:disabled {
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.arco-input-focus) {
|
||||||
|
border: 1px solid var(--Brand-Brand-6, rgba(109, 76, 254, 1));
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(109, 76, 254, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.upload-button {
|
||||||
|
width: 104px;
|
||||||
|
height: 24px;
|
||||||
|
margin-left: 12px;
|
||||||
|
:deep(.arco-btn) {
|
||||||
|
width: 104px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 12px;
|
||||||
|
border: 1px solid var(--BG-500, rgba(177, 178, 181, 1));
|
||||||
|
font-family: Alibaba PuHuiTi, serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Container from '@/views/components/workplace/modules/container.vue';
|
import Container from '@/components/container.vue';
|
||||||
import Product from '@/views/components/workplace/modules/product.vue';
|
import Product from '@/views/components/workplace/modules/product.vue';
|
||||||
import Case from '@/views/components/workplace/modules/case.vue';
|
import Case from '@/views/components/workplace/modules/case.vue';
|
||||||
import { fetchProductList, fetchSuccessCaseList } from '@/api/all/index';
|
import { fetchProductList, fetchSuccessCaseList } from '@/api/all/index';
|
||||||
@ -44,8 +44,8 @@ const getSuccessCaseList = async () => {
|
|||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.body {
|
.body {
|
||||||
padding-left: 0;
|
padding-left: 4px !important;
|
||||||
:deep(> .title) {
|
:deep(> div > .title) {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,14 +65,7 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</div>
|
</div>
|
||||||
<a-modal v-model:visible="visible">
|
<CustomerServiceModal v-model:visible="visible" />
|
||||||
<template #title>
|
|
||||||
扫描下面二维码联系客户
|
|
||||||
</template>
|
|
||||||
<div class="text-center">
|
|
||||||
<img width="200" src="@/assets/customer-service.svg" alt="" />
|
|
||||||
</div>
|
|
||||||
</a-modal>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -80,6 +73,8 @@
|
|||||||
import { now } from '@vueuse/core';
|
import { now } from '@vueuse/core';
|
||||||
import { trialProduct } from '@/api/all';
|
import { trialProduct } from '@/api/all';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
import CustomerServiceModal from '@/components/customer-service-modal.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
product: Product;
|
product: Product;
|
||||||
}>();
|
}>();
|
||||||
|
|||||||
@ -4911,6 +4911,11 @@ vm2@^3.9.8:
|
|||||||
acorn "^8.7.0"
|
acorn "^8.7.0"
|
||||||
acorn-walk "^8.2.0"
|
acorn-walk "^8.2.0"
|
||||||
|
|
||||||
|
vue-cropper@^1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.4.tgz"
|
||||||
|
integrity sha512-5m98vBsCEI9rbS4JxELxXidtAui3qNyTHLHg67Qbn7g8cg+E6LcnC+hh3SM/p94x6mFh6KRxT1ttnta+wCYqWA==
|
||||||
|
|
||||||
vue-demi@*, vue-demi@^0.13.11:
|
vue-demi@*, vue-demi@^0.13.11:
|
||||||
version "0.13.11"
|
version "0.13.11"
|
||||||
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz"
|
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user