新增:开发者中心功能、md编辑器等。
This commit is contained in:
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
# https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
||||
@@ -1,3 +1,3 @@
|
||||
# 应用模块接口
|
||||
#API_BASE=http://127.0.0.1:9001/api
|
||||
API_BASE=https://cms-api.websoft.top/api
|
||||
API_BASE=http://127.0.0.1:9001/api
|
||||
#API_BASE=https://cms-api.websoft.top/api
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 基础模块
|
||||
VITE_SERVER_URL=https://server.gxwebsoft.com/api
|
||||
VITE_SERVER_URL=https://common-api.websoft.top/api
|
||||
# 应用模块
|
||||
API_BASE=https://cms-api.websoft.top/api
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface CmsOrder {
|
||||
type?: number;
|
||||
// 关联文章ID
|
||||
articleId?: number;
|
||||
// 关联网站ID
|
||||
websiteId?: number;
|
||||
// 真实姓名
|
||||
realName?: string;
|
||||
// 手机号码
|
||||
@@ -46,6 +48,8 @@ export interface CmsOrder {
|
||||
userId?: number;
|
||||
// 备注
|
||||
comments?: string;
|
||||
// 附件
|
||||
files?: string;
|
||||
// 排序号
|
||||
sortNumber?: number;
|
||||
// 是否删除, 0否, 1是
|
||||
@@ -65,5 +69,6 @@ export interface CmsOrder {
|
||||
*/
|
||||
export interface CmsOrderParam extends PageParam {
|
||||
orderId?: number;
|
||||
websiteId?: number;
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import request from '~/utils/request';
|
||||
import type { ApiResult, PageResult } from '@/api';
|
||||
import type { CmsWebsite, CmsWebsiteParam } from './model';
|
||||
import {COMMON_API_URL} from "~/config";
|
||||
|
||||
|
||||
/**
|
||||
@@ -63,6 +64,20 @@ export async function updateCmsWebsite(data: CmsWebsite) {
|
||||
return Promise.reject(new Error(res.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改网站信息记录表
|
||||
*/
|
||||
export async function updateCmsWebsiteAll(data: CmsWebsite) {
|
||||
const res = await request.put<ApiResult<unknown>>(
|
||||
'http://127.0.0.1:9002/api/cms/cms-website/updateAll',
|
||||
data
|
||||
);
|
||||
if (res.code === 0) {
|
||||
return res.message;
|
||||
}
|
||||
return Promise.reject(new Error(res.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除网站信息记录表
|
||||
*/
|
||||
@@ -97,7 +112,7 @@ export async function removeBatchCmsWebsite(data: (number | undefined)[]) {
|
||||
*/
|
||||
export async function getCmsWebsiteAll(id: number) {
|
||||
const res = await request.get<ApiResult<CmsWebsite>>(
|
||||
'/cms/cms-website/all/' + id
|
||||
'/cms/cms-website/getAll/' + id
|
||||
);
|
||||
if (res.code === 0 && res.data) {
|
||||
return res.data;
|
||||
|
||||
@@ -43,6 +43,12 @@ export interface CmsWebsite {
|
||||
adminUrl?: string;
|
||||
// 应用版本 10免费版 20专业版 30永久授权
|
||||
version?: number;
|
||||
// 应用价格
|
||||
price?: string,
|
||||
// 交付方式
|
||||
deliveryMethod: number,
|
||||
// 计费方式
|
||||
chargingMethod: number,
|
||||
// 服务到期时间
|
||||
expirationTime?: string;
|
||||
// 模版ID
|
||||
@@ -77,8 +83,14 @@ export interface CmsWebsite {
|
||||
policeNo?: string;
|
||||
// 备注
|
||||
comments?: string;
|
||||
// 详细介绍
|
||||
content?: any;
|
||||
// 是否推荐
|
||||
recommend?: number;
|
||||
// 是否官方
|
||||
official?: boolean;
|
||||
// 是否显示在插件市场
|
||||
market?: boolean;
|
||||
// 运行状态
|
||||
running?: number;
|
||||
// 状态 0未开通 1运行中 2维护中 3已关闭 4已欠费停机 5违规关停
|
||||
@@ -117,5 +129,9 @@ export interface CmsWebsiteParam extends PageParam {
|
||||
websiteId?: number;
|
||||
status?: number;
|
||||
recommend?: number;
|
||||
official?: boolean;
|
||||
market?: boolean;
|
||||
websiteType?: string;
|
||||
userId?: number;
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function getSiteInfo(params: CmsWebsiteParam) {
|
||||
*/
|
||||
export async function getUserInfo(): Promise<User> {
|
||||
const config = useRuntimeConfig();
|
||||
const res = await request.get<ApiResult<User>>(config.public.apiServer + '/auth/user',{
|
||||
const res = await request.get<ApiResult<User>>(COMMON_API_URL + '/auth/user',{
|
||||
headers: {
|
||||
TenantId: `${localStorage.getItem('ServerTenantId')}`
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const config = useConfigInfo();
|
||||
<div class="text-base text-gray-400 hover:text-gray-200 py-3 font-bold">{{ item.title }}</div>
|
||||
<div class="sub-menu flex flex-col">
|
||||
<template v-for="(sub,subIndex) in item.children">
|
||||
<nuxt-link :to="navTo(sub)" :target="sub.target" class="py-1 text-sm"><span class="text-gray-400 hover:text-gray-200">{{ sub.title }}</span></nuxt-link>
|
||||
<nuxt-link :to="navTo(sub)" class="py-1 text-sm"><span class="text-gray-400 hover:text-gray-200">{{ sub.title }}</span></nuxt-link>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-space class="flex items-center cursor-pointer">
|
||||
<el-avatar class="cursor-pointer" :src="user?.avatar" :size="30" />
|
||||
<span>{{ user?.tenantName }}</span>
|
||||
<span>{{ user?.nickname }}</span>
|
||||
</el-space>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user"><nuxt-link to="/user">用户中心</nuxt-link></el-dropdown-item>
|
||||
<!-- <el-dropdown-item command="password"><nuxt-link to="/user/password">修改密码</nuxt-link></el-dropdown-item>-->
|
||||
<el-dropdown-item command="password"><nuxt-link to="/user/password">修改密码</nuxt-link></el-dropdown-item>
|
||||
<el-dropdown-item command="auth"><nuxt-link to="/user/auth">实名认证</nuxt-link></el-dropdown-item>
|
||||
<el-dropdown-item divided command="order"><nuxt-link to="/user/order">我的订单</nuxt-link></el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut"><nuxt-link to="/user/logout">退出登录</nuxt-link>
|
||||
|
||||
109
components/ByteMdEditor/index.vue
Normal file
109
components/ByteMdEditor/index.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<!-- markdown 编辑器 -->
|
||||
<template>
|
||||
<div ref="rootRef" class="ele-bytemd-wrap"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { Editor } from 'bytemd';
|
||||
import type { BytemdPlugin, BytemdLocale, ViewerProps } from 'bytemd';
|
||||
import 'bytemd/dist/index.min.css';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value: string;
|
||||
plugins?: BytemdPlugin[];
|
||||
sanitize?: (schema: any) => any;
|
||||
mode?: 'split' | 'tab' | 'auto';
|
||||
previewDebounce?: number;
|
||||
placeholder?: string;
|
||||
editorConfig?: Record<string, any>;
|
||||
locale?: Partial<BytemdLocale>;
|
||||
uploadImages?: (
|
||||
files: File[]
|
||||
) => Promise<Pick<any, 'url' | 'alt' | 'title'>[]>;
|
||||
overridePreview?: (el: HTMLElement, props: ViewerProps) => void;
|
||||
maxLength?: number;
|
||||
height?: string;
|
||||
fullZIndex?: number;
|
||||
}>(),
|
||||
{
|
||||
fullZIndex: 999
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', value?: string): void;
|
||||
(e: 'change', value?: string): void;
|
||||
}>();
|
||||
|
||||
const rootRef = ref<HTMLElement | null>(null);
|
||||
const editor = ref<InstanceType<typeof Editor> | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
editor.value = new Editor({
|
||||
target: rootRef.value as HTMLElement,
|
||||
props
|
||||
});
|
||||
editor.value.$on('change', (e: any) => {
|
||||
emit('update:value', e.detail.value);
|
||||
emit('change', e.detail.value);
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
[
|
||||
() => props.value,
|
||||
() => props.plugins,
|
||||
() => props.sanitize,
|
||||
() => props.mode,
|
||||
() => props.previewDebounce,
|
||||
() => props.placeholder,
|
||||
() => props.editorConfig,
|
||||
() => props.locale,
|
||||
() => props.uploadImages,
|
||||
() => props.maxLength
|
||||
],
|
||||
() => {
|
||||
const option = { ...props };
|
||||
for (let key in option) {
|
||||
if (typeof option[key] === 'undefined') {
|
||||
delete option[key];
|
||||
}
|
||||
}
|
||||
editor.value?.$set(option);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// 修改编辑器高度
|
||||
.ele-bytemd-wrap :deep(.bytemd) {
|
||||
height: v-bind(height);
|
||||
|
||||
// 修改全屏的 zIndex
|
||||
&.bytemd-fullscreen {
|
||||
z-index: v-bind(fullZIndex);
|
||||
}
|
||||
|
||||
// 去掉默认的最大宽度限制
|
||||
.CodeMirror .CodeMirror-lines {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
pre.CodeMirror-line,
|
||||
pre.CodeMirror-line-like {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
max-width: 100%;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
// 去掉 github 图标
|
||||
.bytemd-toolbar-right > .bytemd-toolbar-icon:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,98 +0,0 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl m-auto py-4">
|
||||
<div v-if="title" class="text-center flex flex-col items-center z-100 relative">
|
||||
<h2 class="text-4xl font-bold tracking-tight text-gray-500 dark:text-white">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="6" class="left mb-8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class=" hover:bg-gray-50 cursor-pointer" @click="navigateTo(`/market/${item.websiteId}.html`)">
|
||||
<div class="flex-1 px-4 py-5 sm:p-4 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex gap-1.5">
|
||||
<el-avatar
|
||||
:src="item.websiteLogo" shape="square" :size="55" style="background-color: white;"/>
|
||||
<div class="flex-1 text-lg cursor-pointer flex flex-col">
|
||||
{{ item.websiteName }}
|
||||
<div class="flex justify-between items-center">
|
||||
<sapn class="text-xs text-gray-400 font-normal line-clamp-1">{{ item.comments || '暂无描述' }}</sapn>
|
||||
<el-button size="small" round>获取</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-image pt-3">
|
||||
<el-image v-if="item.files" :src="`${JSON.parse(item.files)[0].url}`" class="w-full h-1/2 max-h-[220px]" />
|
||||
<el-image v-else class="w-full h-[220px]" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-row :gutter="24" class="flex w-full">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="6" :xs="24" class="mb-6">
|
||||
<nuxt-link :to="item.path">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-green-50 cursor-pointer h-[200px] flex items-center justify-center">
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<el-space class="flex-1 cursor-pointer flex items-center flex-col">
|
||||
<el-image v-if="item.icon" :src="item.icon" class="w-[40px]" />
|
||||
<span class="py-3 text-lg">{{ item.title }}</span>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</nuxt-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {loginAdminByToken} from "~/utils/common";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import {pageCompanyAll} from "~/api/system/company";
|
||||
import {listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import {navigateTo} from "#imports";
|
||||
import type {CmsWebsite} from "~/api/cms/cmsWebsite/model";
|
||||
import {listWebsite} from "~/api/system/website";
|
||||
import {listCmsWebsite, pageCmsWebsiteAll} from "~/api/cms/cmsWebsite";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
param?: CompanyParam;
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const list = ref<CmsWebsite[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
pageCmsWebsiteAll({
|
||||
}).then(data => {
|
||||
list.value = data?.list || [];
|
||||
})
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<header class="clearfix flex justify-between items-center w-full">
|
||||
<div class="logo" :class="logo?.style">
|
||||
<nuxt-link :to="`/m`" target="_top"><img :src="`${logo?.value }`" /></nuxt-link>
|
||||
<nuxt-link :to="`/m`" target="_top"><el-image :src="logo?.value" class=" rounded-sm rounded-sm w-[107px] h-[40px]"/></nuxt-link>
|
||||
</div>
|
||||
<div class="sub-menu">
|
||||
<el-icon color="black" :size="20" @click="drawer = true"><Fold /></el-icon>
|
||||
@@ -49,14 +49,14 @@
|
||||
<script setup lang="ts">
|
||||
// 引入所需的图标
|
||||
import {
|
||||
useConfigInfo,
|
||||
useConfigInfo, useLogo,
|
||||
useMenu,
|
||||
useToken,
|
||||
useUser, useWebsite
|
||||
} from "~/composables/configState";
|
||||
import {getPath, isMobileDevice, navTo} from "#imports";
|
||||
import { Search, Expand, Fold } from '@element-plus/icons-vue'
|
||||
import {configWebsiteField} from "~/api/cms/cmsWebsiteField";
|
||||
import {configWebsiteField, listCmsWebsiteField} from "~/api/cms/cmsWebsiteField";
|
||||
import {getSiteInfo, getUserInfo} from "~/api/layout";
|
||||
import {listCmsLangLog} from "~/api/cms/cmsLangLog";
|
||||
import type {CmsLangLog} from "~/api/cms/cmsLangLog/model";
|
||||
@@ -71,7 +71,7 @@ const i18n = useI18n();
|
||||
const isMobile = ref<boolean>(false);
|
||||
const langList = ref<CmsLangLog[]>([]);
|
||||
const drawer = ref<boolean>(false);
|
||||
const logo = ref<CmsWebsiteField>();
|
||||
const logo = useLogo()
|
||||
const keyword = ref();
|
||||
|
||||
const setNav = () => {
|
||||
@@ -102,9 +102,12 @@ const reload = async () => {
|
||||
langList.value = list;
|
||||
})
|
||||
|
||||
// TODO 读取logo
|
||||
getWebsiteField(13686).then(data => {
|
||||
logo.value = data;
|
||||
listCmsWebsiteField({
|
||||
name: 'siteLogo'
|
||||
}).then(data => {
|
||||
if(data[0]){
|
||||
logo.value = data[0]
|
||||
}
|
||||
})
|
||||
|
||||
// TODO 是否跳转H5版
|
||||
|
||||
83
components/SiteList.vue
Normal file
83
components/SiteList.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl m-auto py-4">
|
||||
<div v-if="title" class="text-center flex flex-col items-center z-100 relative">
|
||||
<h2 class="text-4xl font-bold tracking-tight text-gray-500 dark:text-white">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="6" class="left mb-8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="h-[200px] items-center flex justify-center" @mouseover="showMenu(item)" @mouseleave="hideMenu">
|
||||
<div class="flex-1 px-4 py-5 sm:p-4 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col items-center gap-1.5">
|
||||
<el-avatar
|
||||
:src="item.websiteLogo" shape="square" :size="55" style="background-color: white;"/>
|
||||
<div class="flex-1 cursor-pointer flex flex-col text-center">
|
||||
<nuxt-link :to="`https://${item.websiteCode}.websoft.top`" class="text-lg">{{ item.websiteName }}</nuxt-link>
|
||||
<div v-if="id == item.websiteId" class="flex text-gray-400 text-sm font-normal py2 justify-between items-center">
|
||||
<div>
|
||||
<nuxt-link :to="`https://websoft.top/docs?appid=${item.websiteId}`"><span class="text-gray-400 hover:text-green-700">开发文档</span></nuxt-link>
|
||||
<el-divider direction="vertical" />
|
||||
<nuxt-link :to="`https://websoft.top/docs?appid=${item.websiteId}`"><span class="text-gray-400 hover:text-green-700">社区</span></nuxt-link>
|
||||
<el-divider direction="vertical" />
|
||||
<nuxt-link :to="`https://websoft.top/market?appid=${item.websiteId}`"><span class="text-gray-400 hover:text-green-700">插件市场</span></nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {navigateTo} from "#imports";
|
||||
import type {CmsWebsite, CmsWebsiteParam} from "~/api/cms/cmsWebsite/model";
|
||||
import {pageCmsWebsiteAll} from "~/api/cms/cmsWebsite";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
param?: CmsWebsiteParam;
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const list = ref<CmsWebsite[]>([]);
|
||||
const id = ref<number>(0);
|
||||
|
||||
const showMenu = (item: CmsWebsite) => {
|
||||
id.value = Number(item.websiteId);
|
||||
};
|
||||
|
||||
const hideMenu = () => {
|
||||
id.value = 0;
|
||||
};
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
pageCmsWebsiteAll(props.param).then(data => {
|
||||
list.value = data?.list || [];
|
||||
})
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
242
components/TinymceEditor/index.vue
Normal file
242
components/TinymceEditor/index.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<!-- 富文本编辑器 -->
|
||||
<template>
|
||||
<component v-if="inlineEditor" :is="tagName" :id="elementId" />
|
||||
<textarea v-else :id="elementId"></textarea>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
watch,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
onActivated,
|
||||
onDeactivated,
|
||||
nextTick,
|
||||
useAttrs
|
||||
} from 'vue';
|
||||
import tinymce from 'tinymce/tinymce';
|
||||
import type {
|
||||
Editor as TinyMCEEditor,
|
||||
EditorEvent,
|
||||
RawEditorSettings
|
||||
} from 'tinymce';
|
||||
import 'tinymce/themes/silver';
|
||||
import 'tinymce/icons/default';
|
||||
import 'tinymce/plugins/code';
|
||||
import 'tinymce/plugins/preview';
|
||||
import 'tinymce/plugins/fullscreen';
|
||||
import 'tinymce/plugins/paste';
|
||||
import 'tinymce/plugins/searchreplace';
|
||||
import 'tinymce/plugins/save';
|
||||
import 'tinymce/plugins/autosave';
|
||||
import 'tinymce/plugins/link';
|
||||
import 'tinymce/plugins/autolink';
|
||||
import 'tinymce/plugins/image';
|
||||
import 'tinymce/plugins/media';
|
||||
import 'tinymce/plugins/table';
|
||||
import 'tinymce/plugins/codesample';
|
||||
import 'tinymce/plugins/lists';
|
||||
import 'tinymce/plugins/advlist';
|
||||
import 'tinymce/plugins/hr';
|
||||
import 'tinymce/plugins/charmap';
|
||||
import 'tinymce/plugins/emoticons';
|
||||
import 'tinymce/plugins/anchor';
|
||||
import 'tinymce/plugins/directionality';
|
||||
import 'tinymce/plugins/pagebreak';
|
||||
import 'tinymce/plugins/quickbars';
|
||||
import 'tinymce/plugins/nonbreaking';
|
||||
import 'tinymce/plugins/visualblocks';
|
||||
import 'tinymce/plugins/visualchars';
|
||||
import 'tinymce/plugins/wordcount';
|
||||
import 'tinymce/plugins/emoticons/js/emojis';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import {
|
||||
DEFAULT_CONFIG,
|
||||
DARK_CONFIG,
|
||||
uuid,
|
||||
bindHandlers,
|
||||
openAlert
|
||||
} from './util';
|
||||
import type { AlertOption } from './util';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 编辑器唯一 id
|
||||
id?: string;
|
||||
// v-model
|
||||
value?: string;
|
||||
// 编辑器配置
|
||||
init?: RawEditorSettings;
|
||||
// 是否内联模式
|
||||
inline?: boolean;
|
||||
// model events
|
||||
modelEvents?: string;
|
||||
// 内联模式标签名
|
||||
tagName?: string;
|
||||
// 是否禁用
|
||||
disabled?: boolean;
|
||||
// 是否跟随框架主题
|
||||
autoTheme?: boolean;
|
||||
// 不跟随框架主题时是否使用暗黑主题
|
||||
darkTheme?: boolean;
|
||||
}>(),
|
||||
{
|
||||
inline: false,
|
||||
modelEvents: 'change input undo redo',
|
||||
tagName: 'div',
|
||||
autoTheme: true
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', value: string): void;
|
||||
}>();
|
||||
|
||||
const attrs = useAttrs();
|
||||
const themeStore = useThemeStore();
|
||||
const { darkMode } = storeToRefs(themeStore);
|
||||
|
||||
// 编辑器唯一 id
|
||||
const elementId: string = props.id || uuid('tiny-vue');
|
||||
|
||||
// 编辑器实例
|
||||
let editorIns: TinyMCEEditor | null = null;
|
||||
|
||||
// 是否内联模式
|
||||
const inlineEditor: boolean = props.init?.inline || props.inline;
|
||||
|
||||
/* 更新 value */
|
||||
const updateValue = (value: string) => {
|
||||
emit('update:value', value);
|
||||
};
|
||||
|
||||
/* 修改内容 */
|
||||
const setContent = (value?: string) => {
|
||||
if (
|
||||
editorIns &&
|
||||
typeof value === 'string' &&
|
||||
value !== editorIns.getContent()
|
||||
) {
|
||||
editorIns.setContent(value);
|
||||
}
|
||||
};
|
||||
|
||||
/* 渲染编辑器 */
|
||||
const render = () => {
|
||||
const isDark = props.autoTheme ? darkMode.value : props.darkTheme;
|
||||
tinymce.init({
|
||||
...DEFAULT_CONFIG,
|
||||
...(isDark ? DARK_CONFIG : {}),
|
||||
...props.init,
|
||||
selector: `#${elementId}`,
|
||||
readonly: props.disabled,
|
||||
inline: inlineEditor,
|
||||
setup: (editor: TinyMCEEditor) => {
|
||||
editorIns = editor;
|
||||
editor.on('init', (e: EditorEvent<any>) => {
|
||||
// 回显初始值
|
||||
if (props.value) {
|
||||
setContent(props.value);
|
||||
}
|
||||
// v-model
|
||||
editor.on(props.modelEvents, () => {
|
||||
updateValue(editor.getContent());
|
||||
});
|
||||
// valid events
|
||||
bindHandlers(e, attrs, editor);
|
||||
});
|
||||
if (typeof props.init?.setup === 'function') {
|
||||
props.init.setup(editor);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 销毁编辑器 */
|
||||
const destory = () => {
|
||||
if (tinymce != null && editorIns != null) {
|
||||
tinymce.remove(editorIns as any);
|
||||
editorIns = null;
|
||||
}
|
||||
};
|
||||
|
||||
/* 弹出提示框 */
|
||||
const alert = (option?: AlertOption) => {
|
||||
openAlert(editorIns, option);
|
||||
};
|
||||
|
||||
defineExpose({ editorIns, alert });
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(val: string, prevVal: string) => {
|
||||
if (val !== prevVal) {
|
||||
setContent(val);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.disabled,
|
||||
(disable) => {
|
||||
if (editorIns !== null) {
|
||||
if (typeof editorIns.mode?.set === 'function') {
|
||||
editorIns.mode.set(disable ? 'readonly' : 'design');
|
||||
} else {
|
||||
editorIns.setMode(disable ? 'readonly' : 'design');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.tagName,
|
||||
() => {
|
||||
destory();
|
||||
nextTick(() => {
|
||||
render();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
watch(darkMode, () => {
|
||||
if (props.autoTheme) {
|
||||
destory();
|
||||
nextTick(() => {
|
||||
render();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
destory();
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
destory();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body .tox-tinymce-aux {
|
||||
z-index: 19990000;
|
||||
}
|
||||
|
||||
textarea[id^='tiny-vue'] {
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
248
components/TinymceEditor/util.ts
Normal file
248
components/TinymceEditor/util.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import type {
|
||||
Editor as TinyMCEEditor,
|
||||
EditorEvent,
|
||||
RawEditorSettings
|
||||
} from 'tinymce';
|
||||
const BASE_URL = import.meta.env.BASE_URL;
|
||||
|
||||
// 默认加载插件
|
||||
const PLUGINS: string = [
|
||||
'code',
|
||||
'preview',
|
||||
'fullscreen',
|
||||
'paste',
|
||||
'searchreplace',
|
||||
'save',
|
||||
'autosave',
|
||||
'link',
|
||||
'autolink',
|
||||
'image',
|
||||
'media',
|
||||
'table',
|
||||
'codesample',
|
||||
'lists',
|
||||
'advlist',
|
||||
'hr',
|
||||
'charmap',
|
||||
'emoticons',
|
||||
'anchor',
|
||||
'directionality',
|
||||
'pagebreak',
|
||||
'quickbars',
|
||||
'nonbreaking',
|
||||
'visualblocks',
|
||||
'visualchars',
|
||||
'wordcount'
|
||||
].join(' ');
|
||||
|
||||
// 默认工具栏布局
|
||||
const TOOLBAR: string = [
|
||||
'fullscreen',
|
||||
'preview',
|
||||
'code',
|
||||
'codesample',
|
||||
'emoticons',
|
||||
'image',
|
||||
'media',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'|',
|
||||
'forecolor',
|
||||
'backcolor',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikethrough',
|
||||
'|',
|
||||
'alignleft',
|
||||
'aligncenter',
|
||||
'alignright',
|
||||
'alignjustify',
|
||||
'|',
|
||||
'outdent',
|
||||
'indent',
|
||||
'|',
|
||||
'numlist',
|
||||
'bullist',
|
||||
'|',
|
||||
'formatselect',
|
||||
'fontselect',
|
||||
'fontsizeselect',
|
||||
'|',
|
||||
'link',
|
||||
'charmap',
|
||||
'anchor',
|
||||
'pagebreak',
|
||||
'|',
|
||||
'ltr',
|
||||
'rtl'
|
||||
].join(' ');
|
||||
|
||||
// 默认配置
|
||||
export const DEFAULT_CONFIG: RawEditorSettings = {
|
||||
height: 300,
|
||||
branding: false,
|
||||
skin_url: BASE_URL + 'tinymce/skins/ui/oxide',
|
||||
content_css: BASE_URL + 'tinymce/skins/content/default/content.min.css',
|
||||
language_url: BASE_URL + 'tinymce/langs/zh_CN.js',
|
||||
language: 'zh_CN',
|
||||
plugins: PLUGINS,
|
||||
toolbar: TOOLBAR,
|
||||
draggable_modal: true,
|
||||
toolbar_mode: 'sliding',
|
||||
quickbars_insert_toolbar: '',
|
||||
images_upload_handler: (blobInfo: any, success: any, error: any) => {
|
||||
if (blobInfo.blob().size / 1024 > 400) {
|
||||
error('大小不能超过 400KB');
|
||||
return;
|
||||
}
|
||||
success('data:image/jpeg;base64,' + blobInfo.base64());
|
||||
},
|
||||
file_picker_types: 'media',
|
||||
file_picker_callback: () => {}
|
||||
};
|
||||
|
||||
// 暗黑主题配置
|
||||
export const DARK_CONFIG: RawEditorSettings = {
|
||||
skin_url: BASE_URL + 'tinymce/skins/ui/oxide-dark',
|
||||
content_css: BASE_URL + 'tinymce/skins/content/dark/content.min.css'
|
||||
};
|
||||
|
||||
// 支持监听的事件
|
||||
export const VALID_EVENTS = [
|
||||
'onActivate',
|
||||
'onAddUndo',
|
||||
'onBeforeAddUndo',
|
||||
'onBeforeExecCommand',
|
||||
'onBeforeGetContent',
|
||||
'onBeforeRenderUI',
|
||||
'onBeforeSetContent',
|
||||
'onBeforePaste',
|
||||
'onBlur',
|
||||
'onChange',
|
||||
'onClearUndos',
|
||||
'onClick',
|
||||
'onContextMenu',
|
||||
'onCopy',
|
||||
'onCut',
|
||||
'onDblclick',
|
||||
'onDeactivate',
|
||||
'onDirty',
|
||||
'onDrag',
|
||||
'onDragDrop',
|
||||
'onDragEnd',
|
||||
'onDragGesture',
|
||||
'onDragOver',
|
||||
'onDrop',
|
||||
'onExecCommand',
|
||||
'onFocus',
|
||||
'onFocusIn',
|
||||
'onFocusOut',
|
||||
'onGetContent',
|
||||
'onHide',
|
||||
'onInit',
|
||||
'onKeyDown',
|
||||
'onKeyPress',
|
||||
'onKeyUp',
|
||||
'onLoadContent',
|
||||
'onMouseDown',
|
||||
'onMouseEnter',
|
||||
'onMouseLeave',
|
||||
'onMouseMove',
|
||||
'onMouseOut',
|
||||
'onMouseOver',
|
||||
'onMouseUp',
|
||||
'onNodeChange',
|
||||
'onObjectResizeStart',
|
||||
'onObjectResized',
|
||||
'onObjectSelected',
|
||||
'onPaste',
|
||||
'onPostProcess',
|
||||
'onPostRender',
|
||||
'onPreProcess',
|
||||
'onProgressState',
|
||||
'onRedo',
|
||||
'onRemove',
|
||||
'onReset',
|
||||
'onSaveContent',
|
||||
'onSelectionChange',
|
||||
'onSetAttrib',
|
||||
'onSetContent',
|
||||
'onShow',
|
||||
'onSubmit',
|
||||
'onUndo',
|
||||
'onVisualAid'
|
||||
];
|
||||
|
||||
let unique = 0;
|
||||
|
||||
/**
|
||||
* 生成编辑器 id
|
||||
*/
|
||||
export function uuid(prefix: string): string {
|
||||
const time = Date.now();
|
||||
const random = Math.floor(Math.random() * 1000000000);
|
||||
unique++;
|
||||
return prefix + '_' + random + unique + String(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定事件
|
||||
*/
|
||||
export function bindHandlers(
|
||||
initEvent: EditorEvent<any>,
|
||||
listeners: Record<string, any>,
|
||||
editor: TinyMCEEditor
|
||||
): void {
|
||||
const validEvents = VALID_EVENTS.map((event) => event.toLowerCase());
|
||||
Object.keys(listeners)
|
||||
.filter((key: string) => validEvents.includes(key.toLowerCase()))
|
||||
.forEach((key: string) => {
|
||||
const handler = listeners[key];
|
||||
if (typeof handler === 'function') {
|
||||
if (key === 'onInit') {
|
||||
handler(initEvent, editor);
|
||||
} else {
|
||||
editor.on(key.substring(2), (e: EditorEvent<any>) =>
|
||||
handler(e, editor)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出提示框
|
||||
*/
|
||||
export function openAlert(
|
||||
editor: TinyMCEEditor | null,
|
||||
option: AlertOption = {}
|
||||
) {
|
||||
editor?.windowManager?.open({
|
||||
title: option.title ?? '提示',
|
||||
body: {
|
||||
type: 'panel',
|
||||
items: [
|
||||
{
|
||||
type: 'htmlpanel',
|
||||
html: `<p>${option.content ?? ''}</p>`
|
||||
}
|
||||
]
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
type: 'cancel',
|
||||
name: 'closeButton',
|
||||
text: '确定',
|
||||
primary: true
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
export interface AlertOption {
|
||||
title?: string;
|
||||
content?: string;
|
||||
}
|
||||
@@ -5,14 +5,12 @@ export const APP_SECRET = 'ffd6eee985af45e4a75098422d1decbb';
|
||||
|
||||
export const domain = 'https://websoft.top';
|
||||
// 主节点
|
||||
export const SERVER_API_URL = import.meta.env.VITE_SERVER_URL || 'https://server.gxwebsoft.com/api';
|
||||
export const SERVER_API_URL = import.meta.env.VITE_SERVER_URL || 'https://common-api.websoft.top/api';
|
||||
// 模块节点
|
||||
export const MODULES_API_URL = import.meta.env.VITE_API_URL || 'https://server.gxwebsoft.com/api';
|
||||
export const MODULES_API_URL = import.meta.env.VITE_API_URL || 'https://cms-api.websoft.top/api';
|
||||
export const COMMON_API_URL = import.meta.env.VITE_THINK_URL || 'https://common-api.websoft.top/api';
|
||||
// 文件服务器地址
|
||||
export const FILE_SERVER = 'https://file.wsdns.cn';
|
||||
// 图片前缀
|
||||
export const IMG_URL = 'https://gxtyzx.gxsportscenter.com/uploads/images/';
|
||||
|
||||
/**
|
||||
* 以下配置一般不需要修改
|
||||
|
||||
935
package-lock.json
generated
935
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,7 @@
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"js-md5": "^0.7.3",
|
||||
"less": "^4.1.3",
|
||||
"md-editor-v3": "^5.2.2",
|
||||
"nuxt": "^3.12.4",
|
||||
"qrcode": "^1.5.4",
|
||||
"qrcode.vue": "^3.3.3",
|
||||
|
||||
466
pages/developer/[id].vue
Normal file
466
pages/developer/[id].vue
Normal file
@@ -0,0 +1,466 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl m-auto py-4 my-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 插件编辑 </span>
|
||||
</template>
|
||||
|
||||
<div class="login-layout mt-10 sm:w-screen-xl w-full">
|
||||
<el-form :model="form" label-width="auto" size="large">
|
||||
<el-tabs
|
||||
v-model="activeName"
|
||||
type="border-card"
|
||||
class="demo-tabs bg-white"
|
||||
>
|
||||
<el-tab-pane label="基本信息" name="info">
|
||||
<el-form-item label="插件ID" class="px-4" label-width="100" label-position="left">
|
||||
<el-input disabled v-model="form.websiteId"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="插件标识" class="px-4" label-width="100" label-position="left">
|
||||
<el-input v-model="form.websiteCode" :disabled="form.websiteCode != ''"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="插件名称" class="px-4" label-width="100" label-position="left">
|
||||
<el-input v-model="form.websiteName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="域名" class="px-4" label-width="100" label-position="left">
|
||||
<el-input v-model="form.domain" placeholder="访问域名"/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="后台管理" class="px-4" label-width="100" label-position="left">-->
|
||||
<!-- <el-input v-model="form.adminUrl" placeholder="site.websoft.top"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="插件描述" class="px-4" label-width="100" label-position="left">
|
||||
<el-input v-model="form.comments" type="textarea" placeholder="插件描述" :rows="4"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" class="px-4" label-width="100" label-position="left">
|
||||
<el-select
|
||||
v-model="form.websiteType"
|
||||
multiple
|
||||
placeholder="选择类型(多选)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in types"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交付方式" class="px-4" label-width="100" label-position="left">
|
||||
<el-input v-model="form.deliveryMethod" placeholder="交付方式"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="计费方式" class="px-4" label-width="100" label-position="left">
|
||||
<el-input v-model="form.chargingMethod" placeholder="计费方式"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="插件价格" class="px-4" label-width="100" label-position="left">
|
||||
<el-input v-model="form.price" placeholder="插件价格"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="插件图标" class="px-4" label-width="100" label-position="left">
|
||||
<el-upload
|
||||
v-model:file-list="avatar"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: 5,
|
||||
}"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="avatarRemove"
|
||||
:on-success="avatarSuccess"
|
||||
>
|
||||
<el-icon>
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item class="px-4" label-width="100" label-position="left">
|
||||
<el-button type="primary" class="sm:w-auto w-full" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="插件截屏" name="files">
|
||||
<el-upload
|
||||
v-model:file-list="files"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: 5,
|
||||
}"
|
||||
:limit="8"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="filesRemove"
|
||||
:on-success="filesSuccess"
|
||||
>
|
||||
<el-icon>
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<el-form-item class="px-4" label-width="100" label-position="left">
|
||||
<el-button type="primary" class="sm:w-auto w-full" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="详细介绍" name="content">
|
||||
<!-- 编辑器 -->
|
||||
<MdEditor v-model="form.content" @onUploadImg="onUploadImg"/>
|
||||
|
||||
<div class="flex flex-col">
|
||||
|
||||
<el-form-item class="my-4" label-position="left">
|
||||
<el-button type="primary" class="sm:w-auto w-full" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="评论管理" name="comments">
|
||||
<Comments :productId="form.companyId" :comments="comments" :count="commentsTotal" @done="doComments"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="插件设置" name="setting">
|
||||
<el-form-item label="允许展示到插件市场" class="px-4" label-width="200" label-position="left">
|
||||
<el-switch v-model="form.market" title="允许展示到插件市场" @change="onSubmit"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否推荐" class="px-4" label-width="200" label-position="left">
|
||||
<el-switch disabled v-model="form.official" @change="onSubmit"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="官方插件" class="px-4" label-width="200" label-position="left">
|
||||
<el-switch disabled v-model="form.official" @change="onSubmit"/>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="统计信息" name="statistic">
|
||||
<Statistic :form="form"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志" name="log">
|
||||
<el-timeline style="max-width: 600px">
|
||||
<el-timeline-item
|
||||
v-for="(activity, index) in logs"
|
||||
:key="index"
|
||||
:timestamp="activity.timestamp"
|
||||
>
|
||||
{{ activity.content }}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-page-header>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {ArrowLeft, View, Search, Plus} from '@element-plus/icons-vue'
|
||||
import type {UploadProps, UploadUserFile} from 'element-plus'
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import {ref} from 'vue'
|
||||
import {getNavIdByParamsId} from "~/utils/common";
|
||||
import type {CmsWebsite} from "~/api/cms/cmsWebsite/model";
|
||||
import {getCmsWebsiteAll, updateCmsWebsite, updateCmsWebsiteAll} from "~/api/cms/cmsWebsite";
|
||||
import Comments from "./components/Comments.vue";
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
import {MdEditor} from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import {uploadOss} from "~/api/system/file";
|
||||
import Statistic from "./components/Statistic.vue";
|
||||
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const tenantId = localStorage.getItem('ServerTenantId')
|
||||
const token = useToken();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const website = useWebsite()
|
||||
const user = useUser();
|
||||
const navId = ref();
|
||||
const activeIndex = ref('');
|
||||
const avatar = ref<UploadUserFile[]>([])
|
||||
const files = ref<UploadUserFile[]>([])
|
||||
const srcList = ref<string[]>([])
|
||||
const dialogImageUrl = ref('')
|
||||
const content = ref('');
|
||||
const dialogVisible = ref(false)
|
||||
const activeName = ref('info')
|
||||
const comments = ref<CompanyComment[]>([]);
|
||||
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsWebsite>({
|
||||
// 站点ID
|
||||
websiteId: undefined,
|
||||
// 网站名称
|
||||
websiteName: undefined,
|
||||
// 网站标识
|
||||
websiteCode: undefined,
|
||||
// 网站LOGO
|
||||
websiteIcon: undefined,
|
||||
// 网站LOGO
|
||||
websiteLogo: undefined,
|
||||
// 网站LOGO(深色模式)
|
||||
websiteDarkLogo: undefined,
|
||||
// 网站类型
|
||||
websiteType: undefined,
|
||||
// 评分
|
||||
rate: undefined,
|
||||
// 点赞数
|
||||
likes: undefined,
|
||||
// 访问量
|
||||
clicks: undefined,
|
||||
// 下载量
|
||||
downloads: undefined,
|
||||
// 网站截图
|
||||
files: undefined,
|
||||
// 网站关键词
|
||||
keywords: undefined,
|
||||
// 域名前缀
|
||||
prefix: undefined,
|
||||
// 绑定域名
|
||||
domain: undefined,
|
||||
// 是否官方
|
||||
official: undefined,
|
||||
// 是否显示在插件市场
|
||||
market: undefined,
|
||||
// 全局样式
|
||||
style: undefined,
|
||||
// 后台管理地址
|
||||
adminUrl: undefined,
|
||||
// 插件版本 10免费版 20专业版 30永久授权
|
||||
version: undefined,
|
||||
// 应用价格
|
||||
price: undefined,
|
||||
// 交付方式
|
||||
deliveryMethod: undefined,
|
||||
// 计费方式
|
||||
chargingMethod: undefined,
|
||||
// 服务到期时间
|
||||
expirationTime: undefined,
|
||||
// 模版ID
|
||||
templateId: undefined,
|
||||
// 行业类型(父级)
|
||||
industryParent: undefined,
|
||||
// 行业类型(子级)
|
||||
industryChild: undefined,
|
||||
// 企业ID
|
||||
companyId: undefined,
|
||||
// 所在国家
|
||||
country: undefined,
|
||||
// 所在省份
|
||||
province: undefined,
|
||||
// 所在城市
|
||||
city: undefined,
|
||||
// 所在辖区
|
||||
region: undefined,
|
||||
// 经度
|
||||
longitude: undefined,
|
||||
// 纬度
|
||||
latitude: undefined,
|
||||
// 街道地址
|
||||
address: undefined,
|
||||
// 联系电话
|
||||
phone: undefined,
|
||||
// 电子邮箱
|
||||
email: undefined,
|
||||
// ICP备案号
|
||||
icpNo: undefined,
|
||||
// 公安备案
|
||||
policeNo: undefined,
|
||||
// 插件介绍
|
||||
content: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 是否推荐
|
||||
recommend: undefined,
|
||||
// 运行状态
|
||||
running: undefined,
|
||||
// 状态 0未开通 1运行中 2维护中 3已关闭 4已欠费停机 5违规关停
|
||||
status: undefined,
|
||||
// 维护说明
|
||||
statusText: undefined,
|
||||
// 关闭说明
|
||||
statusClose: undefined,
|
||||
// 状态图标
|
||||
statusIcon: undefined,
|
||||
// 全局样式
|
||||
styles: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 修改时间
|
||||
updateTime: undefined,
|
||||
// 网站配置
|
||||
config: undefined,
|
||||
topNavs: undefined,
|
||||
bottomNavs: undefined,
|
||||
loginUser: undefined
|
||||
});
|
||||
|
||||
const logs = [
|
||||
{
|
||||
content: '发布插件',
|
||||
timestamp: '2018-04-15',
|
||||
},
|
||||
{
|
||||
content: '更新',
|
||||
timestamp: '2018-04-13',
|
||||
},
|
||||
{
|
||||
content: '更新',
|
||||
timestamp: '2018-04-11',
|
||||
},
|
||||
]
|
||||
|
||||
const types = [
|
||||
{
|
||||
value: '网站',
|
||||
label: '网站',
|
||||
},
|
||||
{
|
||||
value: '小程序',
|
||||
label: '小程序',
|
||||
},
|
||||
{
|
||||
value: 'MacOS',
|
||||
label: 'MacOS',
|
||||
},
|
||||
{
|
||||
value: 'Windows',
|
||||
label: 'Windows',
|
||||
},
|
||||
{
|
||||
value: 'App',
|
||||
label: 'App',
|
||||
},
|
||||
]
|
||||
|
||||
useHead({
|
||||
title: `用户中心`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
getCmsWebsiteAll(navId.value).then(data => {
|
||||
// 获取栏目信息
|
||||
assignFields(data)
|
||||
// 插件头像
|
||||
avatar.value = []
|
||||
if (data.websiteLogo) {
|
||||
avatar.value.push({
|
||||
uid: form.websiteId,
|
||||
url: data.websiteLogo,
|
||||
name: '插件头像',
|
||||
})
|
||||
}
|
||||
|
||||
// 插件截图
|
||||
files.value = []
|
||||
if (data.files) {
|
||||
const imgArr = JSON.parse(data.files);
|
||||
imgArr.map((item: any) => {
|
||||
files.value.push({
|
||||
uid: form.websiteId,
|
||||
url: item,
|
||||
name: '插件截图',
|
||||
})
|
||||
srcList.value.push(item)
|
||||
})
|
||||
}
|
||||
|
||||
if(data.websiteType){
|
||||
form.websiteType = JSON.parse(data.websiteType)
|
||||
}
|
||||
|
||||
// 设置页面标题
|
||||
useSeoMeta({
|
||||
description: data.comments || data.websiteName,
|
||||
keywords: data.websiteName,
|
||||
titleTemplate: `${data?.websiteName}` + ' - %s',
|
||||
})
|
||||
|
||||
|
||||
// 加载评论
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err, '加载失败...')
|
||||
})
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
router.back(); // 返回上一页
|
||||
}
|
||||
|
||||
const avatarRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.websiteLogo = '';
|
||||
}
|
||||
|
||||
const avatarSuccess = (e: any) => {
|
||||
form.websiteLogo = e.data.downloadUrl
|
||||
}
|
||||
|
||||
const filesRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.files = JSON.stringify('');
|
||||
}
|
||||
|
||||
const filesSuccess = (e: any) => {
|
||||
srcList.value.push(e.data.downloadUrl)
|
||||
}
|
||||
|
||||
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
dialogImageUrl.value = uploadFile.url!
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 图片上传
|
||||
const onUploadImg = async (files: any, callback: any) => {
|
||||
const res = await Promise.all(
|
||||
files.map((file: any) => {
|
||||
return new Promise((rev, rej) => {
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
uploadOss(file).then((res: any) => rev(res))
|
||||
.catch((error: any) => rej(error));
|
||||
});
|
||||
})
|
||||
);
|
||||
console.log(res, '上次')
|
||||
callback(res.map((item) => item.url));
|
||||
};
|
||||
|
||||
const onSubmit = () => {
|
||||
form.files = undefined;
|
||||
if (srcList.value.length > 0) {
|
||||
form.files = JSON.stringify(srcList.value)
|
||||
}
|
||||
if(form.websiteType){
|
||||
form.websiteType = JSON.stringify(form.websiteType)
|
||||
}
|
||||
updateCmsWebsiteAll(form).then(() => {
|
||||
ElMessage.success('修改成功');
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
82
pages/developer/components/AppInfo.vue
Normal file
82
pages/developer/components/AppInfo.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div v-if="form" class="app-info flex justify-around items-center">
|
||||
<div class="item text-center">
|
||||
<div class="rate text-gray-400">评分</div>
|
||||
<div class="text-2xl font-bold">3.1</div>
|
||||
<el-rate v-model="form.rate" disabled size="small"/>
|
||||
</div>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<div class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">插件ID</div>
|
||||
<el-icon size="24" class="py-1"><Cpu /></el-icon>
|
||||
<span class="text-gray-500">{{ form.websiteId }}</span>
|
||||
</div>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<nuxt-link :to="`https://${form.domain}`" class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">域名</div>
|
||||
<el-icon size="24" class="py-1"><Compass /></el-icon>
|
||||
<span class="text-gray-500">{{ form.domain }}</span>
|
||||
</nuxt-link>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<nuxt-link :to="`https://${form.tenantId}.wsdns.cn`" class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">开发者</div>
|
||||
<el-icon size="24" class="py-1"><Avatar /></el-icon>
|
||||
<span class="text-gray-500">{{'WebSoft Inc.'}}</span>
|
||||
</nuxt-link>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<div class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">下载次数</div>
|
||||
<!-- <div>#<span class="text-2xl font-bold">13</span></div>-->
|
||||
<el-icon size="24" class="py-1"><Download /></el-icon>
|
||||
<span class="text-gray-500">{{ form.downloads }}</span>
|
||||
</div>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<div class="item text-center">
|
||||
<div class="text-gray-400">大小</div>
|
||||
<div class="text-2xl font-bold">26</div>
|
||||
<span class="text-gray-400">MB</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ArrowLeft,View, Menu, Search,Compass, Cpu,Monitor, Download, Platform, Avatar } from '@element-plus/icons-vue'
|
||||
import type {CmsWebsite} from "~/api/cms/cmsWebsite/model";
|
||||
import {getTenantIdByDomain} from "~/api/cms/cmsDomain";
|
||||
import {listTenant} from "~/api/system/tenant";
|
||||
|
||||
const i18n = useI18n();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: CmsWebsite;
|
||||
value?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', where: any): void
|
||||
}>()
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<any>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const reload = () => {
|
||||
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
</style>
|
||||
195
pages/developer/components/Comments.vue
Normal file
195
pages/developer/components/Comments.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<el-descriptions title="评分及评价">
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onComplaint">投诉</el-button>
|
||||
<el-button type="text" @click="onComments">发表评论</el-button>
|
||||
</template>
|
||||
</el-descriptions>
|
||||
<form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="w-full sm:py-2"
|
||||
size="large"
|
||||
status-icon
|
||||
>
|
||||
<template v-if="comments && comments.length > 0">
|
||||
<div class="w-full">
|
||||
<div v-for="(item,index) in comments" :key="index"
|
||||
class="flex flex-col border-b-2 border-gray-200 pb-2 mb-3"
|
||||
style="border-bottom:1px solid #f3f3f3">
|
||||
<el-space class="user-info flex items-start" style="align-items:normal">
|
||||
<div class="avatar">
|
||||
<el-avatar :src="item.logo"/>
|
||||
</div>
|
||||
<div class="nickname flex flex-col">
|
||||
<el-space class="text-sm text-gray-900">
|
||||
<span class="font-bold">{{ item.tenantName }}</span>
|
||||
<el-rate v-model="item.rate" disabled size="small"/>
|
||||
</el-space>
|
||||
<span class="text-xs text-gray-400">{{ item.createTime }}</span>
|
||||
<div class="comments py-2" v-html="item.comments"></div>
|
||||
<template v-if="item.children" v-for="(sub,index2) in item.children" :key="index2">
|
||||
<el-space class="text-sm text-gray-900">
|
||||
<el-avatar :src="sub.logo" size="small"/>
|
||||
<span class="font-bold">{{ sub.tenantName }}</span>
|
||||
<span class="text-xs text-gray-400">{{ sub.createTime }}</span>
|
||||
</el-space>
|
||||
<div class="comments py-2" v-html="sub.comments"></div>
|
||||
</template>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pagination flex justify-center">
|
||||
<el-pagination background layout="prev, pager, next" size="small" :total="count" @change="onPageChange"/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
暂无用户评论
|
||||
</template>
|
||||
|
||||
<!-- 发表评论 -->
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="发表评论"
|
||||
align-center
|
||||
width="500"
|
||||
:before-close="() => visible = false"
|
||||
>
|
||||
<el-form-item prop="rate">
|
||||
<el-rate v-model="form.rate" size="large" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="comments">
|
||||
<el-input v-model="form.comments" :rows="5" type="textarea"
|
||||
placeholder="最多300字,支持 markdown 语法。请认真填写评论内容,以便于帮助作者更好完善插件,如需反馈问题,请到下方插件提问进行提交。"/>
|
||||
</el-form-item>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)">
|
||||
提交
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 发起投诉 -->
|
||||
<el-dialog
|
||||
v-model="visible2"
|
||||
title="为什么举报此内容?"
|
||||
align-center
|
||||
width="500"
|
||||
:before-close="() => visible2 = false"
|
||||
>
|
||||
<el-checkbox-group v-model="checkList">
|
||||
<el-checkbox label="与我无关" value="与我无关"/>
|
||||
<el-checkbox label="文章过时" value="文章过时"/>
|
||||
<el-checkbox label="标题有误" value="标题有误"/>
|
||||
<el-checkbox label="图像质量差或视觉缺陷" value="图像质量差或视觉缺陷"/>
|
||||
<el-checkbox label="垃圾邮件" value="垃圾邮件"/>
|
||||
<el-checkbox label="成人或违法违规内容" value="成人或违法违规内容"/>
|
||||
<el-checkbox label="侵犯知识产权" value="侵犯知识产权"/>
|
||||
</el-checkbox-group>
|
||||
<div class="py-3">
|
||||
<el-input v-model="form.comments" :rows="5" type="textarea"
|
||||
placeholder="在此处输入反馈。请记住不要包含个人信息,如电话号码。"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible2 = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)">
|
||||
提交
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {FullScreen} from '@element-plus/icons-vue'
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {FormInstance, FormRules} from "element-plus";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import {reactive, ref} from "vue";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
companyId?: number;
|
||||
comments?: CompanyComment[];
|
||||
count?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const visible = ref<boolean>(false);
|
||||
const visible2 = ref<boolean>(false);
|
||||
const checkList = ref<string[]>([]);
|
||||
const loading = ref<boolean>(true)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', page: number): void
|
||||
}>()
|
||||
|
||||
// 配置信息
|
||||
const {form, resetFields} = useFormData<CompanyComment>({
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
userId: undefined,
|
||||
companyId: undefined,
|
||||
rate: undefined,
|
||||
sortNumber: undefined,
|
||||
comments: undefined,
|
||||
status: undefined,
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<any>>({
|
||||
rate: [
|
||||
{required: true, message: '请输入评分', trigger: 'blur'},
|
||||
],
|
||||
comments: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
})
|
||||
|
||||
const onComments = () => {
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
const onComplaint = () => {
|
||||
visible2.value = true;
|
||||
}
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
emit('done', page)
|
||||
}
|
||||
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if (form.rate === 0) {
|
||||
ElMessage.error('还没有评分哦!')
|
||||
return false;
|
||||
}
|
||||
form.companyId = Number(getIdBySpm(5));
|
||||
useClientRequest<ApiResult<any>>(`/system/company-comment`, {
|
||||
method: 'POST',
|
||||
body: form
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
visible.value = false
|
||||
resetFields();
|
||||
emit('done',0)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
102
pages/developer/components/PageBanner.vue
Normal file
102
pages/developer/components/PageBanner.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<div class="md:w-screen-xl m-auto py-10">
|
||||
<div class="gap-8 sm:gap-y-16 lg:items-center" v-if="form">
|
||||
<div class="w-full sm:px-0 px-4">
|
||||
<div class="flex flex-1">
|
||||
<template v-if="form.websiteLogo">
|
||||
<el-image :src="form.websiteLogo" shape="square"
|
||||
class="hidden-sm-and-down bg-white w-[128px] h-[128px] cursor-pointer rounded-avatar shadow-sm hover:shadow mr-6"/>
|
||||
</template>
|
||||
<div class="title flex flex-col">
|
||||
<h1
|
||||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-3xl">
|
||||
<el-space>
|
||||
<span>{{ form.websiteName }}</span>
|
||||
</el-space>
|
||||
</h1>
|
||||
<div class="my-1 text-sm text-gray-500 w-auto sm:max-w-3xl max-w-xs flex-1 dark:text-gray-400">
|
||||
{{ form?.comments || desc }}
|
||||
</div>
|
||||
<el-space class="btn">
|
||||
<nuxt-link target="_blank"><el-button type="primary" round>获取</el-button></nuxt-link>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {CmsWebsite} from "~/api/cms/cmsWebsite/model";
|
||||
|
||||
const token = useToken();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: CmsWebsite;
|
||||
value?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void
|
||||
}>()
|
||||
|
||||
const onBuy = (item: Company) => {
|
||||
// if(item.type === 1){
|
||||
// // 插件
|
||||
// openSpmUrl(`/product/checkout`,item,item.productId)
|
||||
// }else {
|
||||
// // 产品
|
||||
// openSpmUrl(`/product/create`,item,item.productId)
|
||||
// }
|
||||
if (!token.value || token.value == '') {
|
||||
ElMessage.error('请先登录');
|
||||
setTimeout(() => {
|
||||
navigateTo(`/product/create`)
|
||||
}, 500)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 安装插件
|
||||
const installPlug = () => {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '安装中...'
|
||||
})
|
||||
useClientRequest<ApiResult<any>>(`/system/menu/install`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
companyId: getIdBySpm(5)
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
setTimeout(() => {
|
||||
ElMessage.success(res.message);
|
||||
loading.close()
|
||||
emit('done')
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.rounded-avatar {
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.rounded-avatar-xs {
|
||||
border-radius: 20px;
|
||||
}
|
||||
</style>
|
||||
45
pages/developer/components/SearchBar.vue
Normal file
45
pages/developer/components/SearchBar.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<el-space class="flex items-center">
|
||||
<el-input v-model="where.keywords" :placeholder="`${$t('searchKeywords')}...`" :suffix-icon="Search" @change="reload"/>
|
||||
</el-space>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
|
||||
const i18n = useI18n();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: CmsArticle;
|
||||
value?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', where: any): void
|
||||
}>()
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<any>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const reload = () => {
|
||||
navigateTo(`/search/${where.keywords}`)
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
</style>
|
||||
74
pages/developer/components/Statistic.vue
Normal file
74
pages/developer/components/Statistic.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<el-row :gutter="24" class="mb-10">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="浏览" :value="38"/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="销量" :value="2"/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="下载次数" :value="54" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="营业额(元)" :value="outputValue"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<el-row :gutter="24" class="mb-10">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总浏览" :value="268500"/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总销量" :value="1253"/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总下载次数" :value="outputValue*128"/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总销售额(元)" :value="562" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {Search} from '@element-plus/icons-vue'
|
||||
import { useTransition } from '@vueuse/core'
|
||||
import { ChatLineRound, Male } from '@element-plus/icons-vue'
|
||||
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
|
||||
|
||||
const i18n = useI18n();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: CmsArticle;
|
||||
value?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', where: any): void
|
||||
}>()
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<any>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 0,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
lang: i18n.locale.value
|
||||
});
|
||||
|
||||
const reload = () => {
|
||||
navigateTo(`/search/${where.keywords}`)
|
||||
}
|
||||
const source = ref(0)
|
||||
const outputValue = useTransition(source, {
|
||||
duration: 1500,
|
||||
})
|
||||
source.value = 1720
|
||||
</script>
|
||||
98
pages/developer/index.vue
Normal file
98
pages/developer/index.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 开发者中心 </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<el-space class="flex items-center">
|
||||
</el-space>
|
||||
</template>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="8" class="left mb-8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class=" hover:bg-white cursor-pointer">
|
||||
<nuxt-link :to="`/developer/${item.websiteId}`">
|
||||
<div class="flex-1 px-4 py-5 sm:p-4 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex gap-1.5">
|
||||
<el-avatar
|
||||
:src="item.websiteLogo" shape="square" :size="55" style="background-color: white;"/>
|
||||
<div class="flex-1 text-lg cursor-pointer flex flex-col">
|
||||
{{ item.websiteName }}
|
||||
<div class="flex justify-between items-center">
|
||||
<sapn class="text-xs text-gray-400 font-normal line-clamp-1">{{ item.comments || '暂无描述' }}</sapn>
|
||||
<el-button size="small" round>编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-image pt-3">
|
||||
<el-image v-if="item.files" :src="`${JSON.parse(item.files)[0]}`" class="w-full h-1/2 max-h-[220px]" />
|
||||
<el-image v-else class="w-full h-[220px]" />
|
||||
</div>
|
||||
</div>
|
||||
</nuxt-link>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-page-header>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowLeft,View,Search } from '@element-plus/icons-vue'
|
||||
import type {CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import {pageCmsWebsiteAll} from "~/api/cms/cmsWebsite";
|
||||
import type {CmsWebsite, CmsWebsiteParam} from "~/api/cms/cmsWebsite/model";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
// 页面信息
|
||||
const list = ref<CmsWebsite[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsWebsiteParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 12,
|
||||
status: undefined,
|
||||
recommend: undefined,
|
||||
categoryId: undefined,
|
||||
userId: Number(localStorage.getItem('UserId')),
|
||||
lang: undefined
|
||||
});
|
||||
|
||||
const goBack = () => {
|
||||
router.back();
|
||||
}
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
await pageCmsWebsiteAll(where).then(response => {
|
||||
if(response?.list){
|
||||
list.value = response?.list;
|
||||
total.value = response.count;
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
// navId.value = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<Flash/>
|
||||
|
||||
<CompanyList title="产品服务" :param="{official: true,recommend: true,limit: 4}" :fit="`cover`" />
|
||||
<SiteList title="产品服务" :param="{official: true,limit: 4,sort: 'websiteId',order:'asc'}" :fit="`cover`" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
292
pages/m/order/test.vue
Normal file
292
pages/m/order/test.vue
Normal file
@@ -0,0 +1,292 @@
|
||||
<template>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="clearfix p-5" style="border-top: #f9f9f9 16px solid;">
|
||||
|
||||
<div class="m-page">
|
||||
<div class="sitemp h-[32px] flex justify-between">
|
||||
<h2>矛盾纠纷收集与解决</h2>
|
||||
</div>
|
||||
<div class="form-box p-5">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120"
|
||||
label-position="top"
|
||||
status-icon
|
||||
>
|
||||
<el-form-item :label="`您的姓名`" prop="realName" class=" p-2 text-left">
|
||||
<el-input v-model="form.realName" class="text-left" :placeholder="`您的姓名`"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`手机号码`" prop="phone" class=" p-2">
|
||||
<el-input v-model="form.phone" :maxlength="11" :placeholder="`手机号码`"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`您的住址`" prop="address" class="p-2 text-left">
|
||||
<el-cascader v-model="value" class="w-full" :options="RegionsData" placeholder="所在城区" @change="handleChange" />
|
||||
<el-input class="mt-2" v-model="form.address" :placeholder="`您的住址`"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`纠纷描述`" prop="content" class=" p-2">
|
||||
<el-input type="textarea" :rows="5" cols="80" v-model="form.content"
|
||||
:placeholder="`您要反映的矛盾纠纷内容`"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`附件上传`" prop="files" class=" p-2">
|
||||
<el-upload
|
||||
v-model:file-list="files"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: 5,
|
||||
}"
|
||||
:limit="8"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="filesRemove"
|
||||
:on-success="filesSuccess"
|
||||
>
|
||||
<el-icon>
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`验证码`" prop="code" class=" p-2">
|
||||
<el-space class="flex">
|
||||
<el-input size="large" :placeholder="$t('order.imgCode')" maxlength="5" v-model="form.code"/>
|
||||
<el-image :alt="$t('order.imgCode')" v-if="captcha" :src="captcha" @click="changeCaptcha"/>
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="ml-2 w-full">
|
||||
<el-button type="primary" size="large" class="block w-full" @click="submitForm(formRef)">
|
||||
{{ $t('order.submit') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<el-image w-full :src="dialogImageUrl" alt="查看证件"/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {ArrowRight, Plus} from '@element-plus/icons-vue'
|
||||
import type {UploadProps, UploadUserFile} from 'element-plus'
|
||||
import type {FormInstance, FormRules} from 'element-plus'
|
||||
import RegionsData from '~/api/json/regions-data.json'
|
||||
import type {CmsOrder} from "~/api/cms/cmsOrder/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import {addCmsOrder} from "~/api/cms/cmsOrder";
|
||||
import {getCaptcha} from "~/api/passport/login";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const token = useToken();
|
||||
const files = ref<UploadUserFile[]>([])
|
||||
|
||||
const srcList = ref<string[]>([])
|
||||
const address = ref<string>()
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogImageUrl = ref('')
|
||||
// 验证码 base64 数据
|
||||
const captcha = ref('');
|
||||
const text = ref<string>('');
|
||||
|
||||
|
||||
const {form, resetFields} = useFormData<CmsOrder>({
|
||||
// 订单号
|
||||
orderId: undefined,
|
||||
// 模型名称
|
||||
model: 'order',
|
||||
// 订单标题
|
||||
title: undefined,
|
||||
// 订单编号
|
||||
orderNo: undefined,
|
||||
// 订单类型,0商城 1询价 2留言
|
||||
type: undefined,
|
||||
// 关联项目ID,配合订单类型使用
|
||||
articleId: undefined,
|
||||
// 真实姓名
|
||||
realName: undefined,
|
||||
// 手机号码
|
||||
phone: undefined,
|
||||
// 电子邮箱
|
||||
email: undefined,
|
||||
// 收货地址
|
||||
address: undefined,
|
||||
// 订单内容
|
||||
content: undefined,
|
||||
// 订单总额
|
||||
totalPrice: '0.00',
|
||||
// 实际付款
|
||||
payPrice: '0.00',
|
||||
// 报价询价
|
||||
price: '0.00',
|
||||
// 购买数量
|
||||
totalNum: undefined,
|
||||
// 二维码地址,保存订单号,支付成功后才生成
|
||||
qrcode: undefined,
|
||||
// 下单渠道,0网站 1小程序 2其他
|
||||
channel: undefined,
|
||||
// 过期时间
|
||||
expirationTime: undefined,
|
||||
// 订单是否已结算(0未结算 1已结算)
|
||||
isSettled: undefined,
|
||||
// 用户id
|
||||
userId: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 图像验证码内容
|
||||
files: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 图像验证码
|
||||
code: '',
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules<CmsOrder>>({
|
||||
title: [
|
||||
{required: true, message: '请输入产品名称', trigger: 'blur'},
|
||||
],
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
address: [
|
||||
{required: true, message: '请填写您的住址', trigger: 'blur'},
|
||||
],
|
||||
realName: [
|
||||
{required: true, message: '请输入联系人姓名', trigger: 'blur'},
|
||||
],
|
||||
content: [
|
||||
{required: true, message: '请输入留言内容', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
const value = ref([])
|
||||
const options = ref<any>([])
|
||||
const props = {
|
||||
expandTrigger: 'hover' as const,
|
||||
}
|
||||
|
||||
const handleChange = (value:any) => {
|
||||
address.value = ''
|
||||
const province = RegionsData.find(item => item.value == value[0]);
|
||||
if(province){
|
||||
address.value = `${province?.label}`
|
||||
const city = province.children?.find(item => item.value == value[1]);
|
||||
if(city){
|
||||
address.value = `${province?.label}${city.label}`
|
||||
const region = city.children?.find(item => item.value == value[2]);
|
||||
if(region){
|
||||
address.value = `${province?.label}${city.label}${region.label}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取图形验证码 */
|
||||
const changeCaptcha = async () => {
|
||||
getCaptcha().then(captchaData => {
|
||||
captcha.value = captchaData.base64;
|
||||
text.value = captchaData.text;
|
||||
})
|
||||
};
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
form.content = ''
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: form.comments || form.title,
|
||||
keywords: form.title,
|
||||
titleTemplate: `${form?.title || '矛盾纠纷收集平台'}` + ' - %s',
|
||||
})
|
||||
changeCaptcha();
|
||||
}
|
||||
|
||||
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
dialogImageUrl.value = uploadFile.url!
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const filesRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.files = JSON.stringify('');
|
||||
}
|
||||
|
||||
const filesSuccess = (e: any) => {
|
||||
srcList.value.push(e.data.downloadUrl)
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if (form.code !== text.value) {
|
||||
reload();
|
||||
ElMessage.error('验证码不正确!');
|
||||
return false;
|
||||
}
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
// form.files = undefined;
|
||||
// if (srcList.value.length > 0) {
|
||||
// form.files = JSON.stringify(srcList.value)
|
||||
// }
|
||||
if (process.client && form.code) {
|
||||
addCmsOrder({
|
||||
...form,
|
||||
files: JSON.stringify(srcList.value) || undefined,
|
||||
address: `${address.value}${form.address}`
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
resetFields();
|
||||
changeCaptcha();
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 在这里放置你想要在组件渲染完成后执行的代码
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
padding-top: 15px;
|
||||
overflow: hidden;
|
||||
text-indent: 2em;
|
||||
}
|
||||
|
||||
.content p {
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
.content img {
|
||||
padding: 10px;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,46 +1,43 @@
|
||||
<template>
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 产品详情 </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<div class="h-[32px]"></div>
|
||||
</template>
|
||||
<PageBanner :form="page" @done="reload"/>
|
||||
<el-divider />
|
||||
<AppInfo :form="form" />
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 产品详情 </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<div class="h-[32px]"></div>
|
||||
</template>
|
||||
<PageBanner :form="page" @done="reload"/>
|
||||
<AppInfo :form="form" />
|
||||
|
||||
<div class="screen-item my-6">
|
||||
<el-descriptions title="截屏" />
|
||||
<el-scrollbar>
|
||||
<div class="flex" v-if="form.files">
|
||||
<div id="screen-item" class="screen-item my-6">
|
||||
<el-descriptions title="截屏" class="mt-5" />
|
||||
<el-scrollbar class="bg-white p-5">
|
||||
<div class="flex" v-if="form.files">
|
||||
<el-image
|
||||
v-for="(item,index) in JSON.parse(form.files)"
|
||||
:key="index"
|
||||
class="scrollbar-item w-[240px] max-h-[625px] mr-4 mb-3"
|
||||
:src="item.url"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="srcList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
v-for="(item,index) in JSON.parse(form.files)"
|
||||
:key="index"
|
||||
class="scrollbar-item w-[240px] max-h-[625px] mr-4 mb-3"
|
||||
:src="item"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="srcList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<p v-html="form?.content || '介绍'" class="content"></p>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
<!-- 评分及评价 -->
|
||||
<Comments :productId="form.companyId" :comments="comments" :count="commentsTotal" @done="doComments" />
|
||||
<div class="h-[100px]"></div>
|
||||
</el-page-header>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<el-descriptions title="详细介绍" class="mt-5" />
|
||||
<MdPreview id="preview-only" class="px-3" :modelValue="form.content" />
|
||||
</div>
|
||||
|
||||
<!-- 评分及评价 -->
|
||||
<Comments :productId="form.companyId" :comments="comments" :count="commentsTotal" @done="doComments" />
|
||||
<div class="h-[100px]"></div>
|
||||
</el-page-header>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowLeft,View, Monitor, Search,Cpu, Platform, Avatar } from '@element-plus/icons-vue'
|
||||
@@ -55,8 +52,9 @@ import Comments from './components/Comments.vue';
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
import {getCmsWebsiteAll} from "~/api/cms/cmsWebsite";
|
||||
import type {CmsWebsite} from "~/api/cms/cmsWebsite/model";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import Banner from "~/components/Banner.vue";
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
// preview.css相比style.css少了编辑器那部分样式
|
||||
import 'md-editor-v3/lib/preview.css';
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
@@ -199,7 +197,7 @@ const goBack = () => {
|
||||
router.back();
|
||||
}
|
||||
|
||||
// 读取导航详情
|
||||
// 读取项目详情
|
||||
const reload = async () => {
|
||||
getCmsWebsiteAll(navId.value).then(data => {
|
||||
// 获取栏目信息
|
||||
@@ -215,7 +213,7 @@ const reload = async () => {
|
||||
if(data.files){
|
||||
const imgArr = JSON.parse(data.files);
|
||||
imgArr.map((item: any) => {
|
||||
srcList.value.push(item.url)
|
||||
srcList.value.push(item)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="form" class="app-info flex justify-around items-center">
|
||||
<div v-if="form" class="app-info bg-white py-5 flex justify-around items-center">
|
||||
<div class="item text-center">
|
||||
<div class="rate text-gray-400">评分</div>
|
||||
<div class="text-2xl font-bold">3.1</div>
|
||||
@@ -7,43 +7,52 @@
|
||||
</div>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<div class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">插件ID</div>
|
||||
<el-icon size="24" class="py-1"><Cpu /></el-icon>
|
||||
<span class="text-gray-500">{{ form.websiteId }}</span>
|
||||
<div class="text-gray-400">类别</div>
|
||||
<el-icon size="24" class="py-1"><Monitor /></el-icon>
|
||||
<span class="text-gray-500">{{ form.industryParent || '网站' }}</span>
|
||||
</div>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<nuxt-link class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">类型</div>
|
||||
<el-icon size="24" class="py-1"><Monitor /></el-icon>
|
||||
<span class="text-gray-500">{{ '小程序' }}</span>
|
||||
<nuxt-link :to="`https://${form.domain}`" class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">域名</div>
|
||||
<div>
|
||||
<svg t="1739721752315" class="icon pt-2" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11933" width="22" height="22"><path d="M319.9 428.9h37.7l-66.1 216.7H259c-28.9-98-43.9-149.1-44.9-154.3-2.1-4.6-3.1-12.4-3.1-23.2h-1c0 4.6-1 12.4-3.1 23.2-2.1 4.6-18.1 56.2-48 154.3h-32.5l-63-216.7h37.7c23.7 91.3 36.6 141.4 38.7 149.6 0 0.5 0.5 1.5 1 3.1 1.5 9.8 2.1 17 2.1 21.2h1c0-7.7 1.5-15.5 4.1-24.3 2.1-5.7 17-55.7 44.9-149.6h35.6c25.8 94.4 39.7 144.5 41.8 149.6 2.6 8.3 4.1 16.5 4.1 24.3h1c0-4.1 0.5-11.4 2.1-21.2 0.5-1.5 1-2.6 1-3.1 2.7-8.2 16.1-58.2 41.4-149.6z m300.8 0h37.7l-66.1 216.7h-32.5c-28.9-98-43.9-149.1-44.9-154.3-2.1-4.6-3.1-12.4-3.1-23.2h-1c0 4.6-1 12.4-3.1 23.2-2.1 4.6-18.1 56.2-48 154.3h-32.5l-63-216.7h37.7c23.7 91.3 36.6 141.4 38.7 149.6 0 0.5 0.5 1.5 1 3.1 1.5 9.8 2.1 17 2.1 21.2h0.5c0-7.7 1.5-15.5 4.1-24.3 2.1-5.7 17-55.7 44.9-149.6h35.6c25.8 94.4 39.7 144.5 41.8 149.6 2.6 8.3 4.1 16.5 4.1 24.3h1c0-4.1 0.5-11.4 2.1-21.2 0.5-1.5 1-2.6 1-3.1 3.2-8.2 16.6-58.2 41.9-149.6z m300.9 0h37.7l-66.1 216.7h-32.5c-28.9-98-43.9-149.1-44.9-154.3-2.1-4.6-3.1-12.4-3.1-23.2h-1c0 4.6-1 12.4-3.1 23.2-2.1 4.6-18.1 56.2-48 154.3h-32.5l-63-216.7h37.7c23.7 91.3 36.6 141.4 38.7 149.6 0 0.5 0.5 1.5 1 3.1 1.5 9.8 2.1 17 2.1 21.2h1c0-7.7 1.5-15.5 4.1-24.3 2.1-5.7 17-55.7 44.9-149.6h35.6C856 523.4 870 573.4 872 578.6c2.6 8.3 4.1 16.5 4.1 24.3h1c0-4.1 0.5-11.4 2.1-21.2 0.5-1.5 1-2.6 1-3.1 2.7-8.3 16.1-58.3 41.4-149.7z m-755.5-39.2C222.9 254 357 158.5 513.4 158.5S803.9 254 860.7 389.7h45.9c-58.8-160-212.6-274-393.2-274s-333.9 114-393.2 274h45.9z m691 299.3c-58.8 131.1-190.4 222.9-343.7 222.9-153.3 0-284.8-91.3-343.7-222.9h-46.4c61.4 155.3 212.6 265.2 390.1 265.2 177 0 328.7-109.9 390.1-265.2h-46.4z" fill="" p-id="11934"></path><path d="M513.4 217.8c-123.2 0-230.1 69.7-283.4 171.9h-14.9c54.4-109.8 167.6-185.2 298.4-185.2" fill="" p-id="11935"></path></svg>
|
||||
</div>
|
||||
<!-- <el-icon size="24" class="py-1"><Compass /></el-icon>-->
|
||||
<span class="text-gray-500">{{ form.domain }}</span>
|
||||
</nuxt-link>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<nuxt-link :to="`https://${form.tenantId}.wsdns.cn`" class="item text-center flex flex-col items-center">
|
||||
<nuxt-link :to="`/market/user/${form.userId}`" class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">开发者</div>
|
||||
<el-icon size="24" class="py-1"><Avatar /></el-icon>
|
||||
<span class="text-gray-500">{{'WebSoft Inc.'}}</span>
|
||||
</nuxt-link>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<div class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">下载次数</div>
|
||||
<!-- <div>#<span class="text-2xl font-bold">13</span></div>-->
|
||||
<el-icon size="24" class="py-1"><Download /></el-icon>
|
||||
<span class="text-gray-500">{{ form.downloads }}</span>
|
||||
</div>
|
||||
<nuxt-link :to="`http://git.gxwebsoft.com`" class="item text-center">
|
||||
<div class="text-gray-400">仓库</div>
|
||||
<div class="text-2xl font-bold">
|
||||
<!-- <svg t="1739721615244" class="icon pt-2" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10773" width="22" height="22"><path d="M512 64c81.636 0 156.8 19.911 225.493 59.733s122.951 94.08 162.773 162.773S960 430.364 960 512s-19.911 156.8-59.733 225.493-94.08 122.951-162.773 162.773S593.636 960 512 960s-156.8-19.911-225.493-59.733-122.951-94.08-162.773-162.773S64 593.636 64 512s19.911-156.8 59.733-225.493 94.08-122.951 162.773-162.773S430.364 64 512 64z m119.467 812.373c57.742-17.92 108.516-48.782 152.32-92.587 43.804-43.805 75.164-94.578 94.08-152.32 18.916-57.742 23.396-117.476 13.44-179.2-9.956-61.724-32.853-117.476-68.693-167.253-35.84-49.778-81.138-88.604-135.893-116.48C631.964 140.658 573.724 126.72 512 126.72s-119.964 13.938-174.72 41.813-100.053 66.702-135.893 116.48-58.738 105.529-68.693 167.253c-9.956 61.724-5.476 121.458 13.44 179.2s50.276 108.516 94.08 152.32c43.804 43.804 94.578 74.667 152.32 92.587h2.987c5.973 0 10.951-1.493 14.933-4.48 3.982-2.987 5.973-7.467 5.973-13.44v-65.707l-20.907 2.987H377.6c-19.911 1.991-38.329-1.991-55.253-11.947S293.974 759.893 288 741.973l-17.92-32.853-11.947-11.947-23.893-17.92c-3.982-3.982-5.973-6.969-5.973-8.96s0.996-3.982 2.987-5.973l14.933-2.987c5.973 0 11.947 1.493 17.92 4.48 5.973 2.987 11.947 5.476 17.92 7.467l11.947 14.933 11.947 11.947c7.964 13.938 17.422 24.889 28.373 32.853 10.951 7.964 23.893 11.449 38.827 10.453 14.933-0.996 29.369-4.48 43.307-10.453 1.991-9.956 4.978-19.413 8.96-28.373 3.982-8.96 8.96-16.427 14.933-22.4-25.884-1.991-49.778-7.467-71.68-16.427-21.902-8.96-40.818-21.404-56.747-37.333-15.929-15.929-26.88-34.844-32.853-56.747-7.964-25.884-11.947-52.764-11.947-80.64 0-17.92 3.484-35.84 10.453-53.76 6.969-17.92 16.427-33.849 28.373-47.787-1.991-7.964-3.484-15.431-4.48-22.4s-1.493-14.933-1.493-23.893 0.996-17.92 2.987-26.88c1.991-8.96 3.982-18.418 5.973-28.373h8.96c7.964 0 16.427 0.996 25.387 2.987s17.422 4.978 25.387 8.96l26.88 14.933 20.907 11.947c63.716-17.92 127.431-17.92 191.147 0l20.907-11.947 26.88-14.933c7.964-3.982 15.929-6.969 23.893-8.96 7.964-1.991 16.924-2.987 26.88-2.987h5.973c3.982 9.956 6.969 19.413 8.96 28.373 1.991 8.96 2.987 17.92 2.987 26.88 0 8.96-0.498 16.924-1.493 23.893s-2.489 14.436-4.48 22.4c11.947 13.938 21.404 29.867 28.373 47.787 6.969 17.92 10.453 35.84 10.453 53.76 0 27.876-3.982 54.756-11.947 80.64-5.973 21.902-16.924 40.818-32.853 56.747-15.929 15.929-35.342 28.373-58.24 37.333-22.898 8.96-47.289 14.436-73.173 16.427 9.956 7.964 16.924 18.418 20.907 31.36 3.982 12.942 5.973 26.382 5.973 40.32v104.533c0 5.973 1.991 10.453 5.973 13.44 3.982 2.987 7.964 4.48 11.947 4.48h5.972z" p-id="10774"></path></svg>-->
|
||||
<svg t="1739720696276" class="icon pt-2" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4176" width="22" height="22"><path d="M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9 23.5 23.2 38.1 55.4 38.1 91v112.5c0.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z" p-id="4177"></path></svg>
|
||||
</div>
|
||||
<span class="text-gray-500">3.12 MB</span>
|
||||
</nuxt-link>
|
||||
<el-divider class="opacity-40" style="height: 40px" direction="vertical" />
|
||||
<div class="item text-center">
|
||||
<div class="text-gray-400">大小</div>
|
||||
<div class="text-2xl font-bold">26</div>
|
||||
<span class="text-gray-400">MB</span>
|
||||
<div class="item text-center flex flex-col items-center">
|
||||
<div class="text-gray-400">插件ID</div>
|
||||
<el-icon size="24" class="py-1"><Cpu /></el-icon>
|
||||
<span class="text-gray-500">{{ form.websiteId }}</span>
|
||||
</div>
|
||||
<!-- <el-divider class="opacity-40" style="height: 40px" direction="vertical" />-->
|
||||
<!-- <div class="item text-center flex flex-col items-center">-->
|
||||
<!-- <div class="text-gray-400">下载次数</div>-->
|
||||
<!-- <el-icon size="24" class="py-1"><Download /></el-icon>-->
|
||||
<!-- <span class="text-gray-500">{{ form.downloads }}</span>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ArrowLeft,View, Menu, Search, Cpu,Monitor, Download, Platform, Avatar } from '@element-plus/icons-vue'
|
||||
import { ArrowLeft,View, Menu, Search,Compass, Cpu,Monitor, Download, Platform, Avatar } from '@element-plus/icons-vue'
|
||||
import type {CmsWebsite} from "~/api/cms/cmsWebsite/model";
|
||||
import {getTenantIdByDomain} from "~/api/cms/cmsDomain";
|
||||
import {listTenant} from "~/api/system/tenant";
|
||||
|
||||
const i18n = useI18n();
|
||||
|
||||
@@ -78,5 +87,3 @@ const reload = () => {
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
</style>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
status-icon
|
||||
>
|
||||
<template v-if="comments && comments.length > 0">
|
||||
<div class="w-full">
|
||||
<div class="w-full p-4 bg-white">
|
||||
<div v-for="(item,index) in comments" :key="index"
|
||||
class="flex flex-col border-b-2 border-gray-200 pb-2 mb-3"
|
||||
style="border-bottom:1px solid #f3f3f3">
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- {{ form }}-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
</template>
|
||||
<template #extra>
|
||||
<el-space class="flex items-center">
|
||||
<!-- <el-select v-model="value" clearable placeholder="筛选" style="width: 240px">-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="item in options"-->
|
||||
<!-- :key="item.value"-->
|
||||
<!-- :label="item.label"-->
|
||||
<!-- :value="item.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<el-select v-model="value" clearable placeholder="类型" style="width: 180px" @change="onTypes">
|
||||
<el-option
|
||||
v-for="item in types"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<!-- <el-button-group v-model:value="where" @tab-click="handleClick" @change="reload">-->
|
||||
<!-- <el-button>综合</el-button>-->
|
||||
<!-- <el-button>最新</el-button>-->
|
||||
<!-- <el-button>免费</el-button>-->
|
||||
<!-- <el-button>付费</el-button>-->
|
||||
<!-- </el-button-group>-->
|
||||
<el-input v-model="where.keywords" style="width: 400px" :placeholder="`插件ID | 插件名称 | 域名`" :suffix-icon="Search" @change="reload"/>
|
||||
<el-button-group v-model:value="where" @tab-click="handleClick" @change="reload">
|
||||
<el-button>综合</el-button>
|
||||
<el-button>最新</el-button>
|
||||
<el-button>免费</el-button>
|
||||
<el-button>付费</el-button>
|
||||
</el-button-group>
|
||||
</el-space>
|
||||
</template>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="8" class="left mb-8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class=" hover:bg-gray-50 cursor-pointer" @click="navigateTo(`/market/${item.websiteId}.html`)">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="cursor-pointer" @mouseover="showDomain(item)" @mouseleave="hideDomain" @click="navigateTo(`/market/${item.websiteId}.html`)">
|
||||
<div class="flex-1 px-4 py-5 sm:p-4 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex gap-1.5">
|
||||
<el-avatar
|
||||
@@ -35,13 +35,15 @@
|
||||
<div class="flex-1 text-lg cursor-pointer flex flex-col">
|
||||
{{ item.websiteName }}
|
||||
<div class="flex justify-between items-center">
|
||||
<sapn class="text-xs text-gray-400 font-normal line-clamp-1">{{ item.comments || '暂无描述' }}</sapn>
|
||||
<sapn class="text-xs text-gray-400 font-normal line-clamp-1">
|
||||
{{ id == item.websiteId ? item.domain : item.comments || '暂无描述' }}
|
||||
</sapn>
|
||||
<el-button size="small" round>获取</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-image pt-3">
|
||||
<el-image v-if="item.files" :src="`${JSON.parse(item.files)[0].url}`" class="w-full h-1/2 max-h-[220px]" />
|
||||
<el-image v-if="item.files" :src="`${JSON.parse(item.files)[0]}`" class="w-full h-1/2 max-h-[220px]" />
|
||||
<el-image v-else class="w-full h-[220px]" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -80,6 +82,7 @@ const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
const activeName = ref('2839');
|
||||
const id = ref<number>();
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
@@ -92,13 +95,45 @@ const where = reactive<CmsWebsiteParam>({
|
||||
limit: 12,
|
||||
status: undefined,
|
||||
recommend: undefined,
|
||||
market: true,
|
||||
websiteType: undefined,
|
||||
categoryId: undefined,
|
||||
lang: undefined
|
||||
});
|
||||
|
||||
const types = [
|
||||
{
|
||||
value: '网站',
|
||||
label: '网站',
|
||||
},
|
||||
{
|
||||
value: '小程序',
|
||||
label: '小程序',
|
||||
},
|
||||
{
|
||||
value: 'MacOS',
|
||||
label: 'MacOS',
|
||||
},
|
||||
{
|
||||
value: 'Windows',
|
||||
label: 'Windows',
|
||||
},
|
||||
{
|
||||
value: 'App',
|
||||
label: 'App',
|
||||
},
|
||||
]
|
||||
|
||||
const goBack = () => {
|
||||
router.back();
|
||||
}
|
||||
const showDomain = (item: CmsWebsite) => {
|
||||
id.value = Number(item.websiteId);
|
||||
};
|
||||
|
||||
const hideDomain = () => {
|
||||
id.value = 0;
|
||||
};
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
@@ -157,28 +192,11 @@ const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
console.log(tab, event)
|
||||
}
|
||||
const value = ref('')
|
||||
const options = [
|
||||
{
|
||||
value: 'Option1',
|
||||
label: 'Option1',
|
||||
},
|
||||
{
|
||||
value: 'Option2',
|
||||
label: 'Option2',
|
||||
},
|
||||
{
|
||||
value: 'Option3',
|
||||
label: 'Option3',
|
||||
},
|
||||
{
|
||||
value: 'Option4',
|
||||
label: 'Option4',
|
||||
},
|
||||
{
|
||||
value: 'Option5',
|
||||
label: 'Option5',
|
||||
},
|
||||
]
|
||||
|
||||
const onTypes = (value: string) => {
|
||||
where.websiteType = value;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
@@ -200,3 +218,5 @@ watch(
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
223
pages/market/user/[id].vue
Normal file
223
pages/market/user/[id].vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
|
||||
<!-- 主体部分 -->
|
||||
<div class="xl:w-screen-xl m-auto py-4 mt-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 相关插件 </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<el-space class="flex items-center">
|
||||
<el-select v-model="value" clearable placeholder="类型" style="width: 180px" @change="onTypes">
|
||||
<el-option
|
||||
v-for="item in types"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<!-- <el-button-group v-model:value="where" @tab-click="handleClick" @change="reload">-->
|
||||
<!-- <el-button>综合</el-button>-->
|
||||
<!-- <el-button>最新</el-button>-->
|
||||
<!-- <el-button>免费</el-button>-->
|
||||
<!-- <el-button>付费</el-button>-->
|
||||
<!-- </el-button-group>-->
|
||||
<el-input v-model="where.keywords" style="width: 400px" :placeholder="`插件ID | 插件名称 | 域名`" :suffix-icon="Search" @change="reload"/>
|
||||
</el-space>
|
||||
</template>
|
||||
<el-row :gutter="24" id="container" class="clearfix">
|
||||
<el-col v-for="(item,index) in list" :key="index" :span="8" class="left mb-8">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="cursor-pointer" @mouseover="showDomain(item)" @mouseleave="hideDomain" @click="navigateTo(`/market/${item.websiteId}.html`)">
|
||||
<div class="flex-1 px-4 py-5 sm:p-4 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex gap-1.5">
|
||||
<el-avatar
|
||||
:src="item.websiteLogo" shape="square" :size="55" style="background-color: white;"/>
|
||||
<div class="flex-1 text-lg cursor-pointer flex flex-col">
|
||||
{{ item.websiteName }}
|
||||
<div class="flex justify-between items-center">
|
||||
<sapn class="text-xs text-gray-400 font-normal line-clamp-1">
|
||||
{{ id == item.websiteId ? item.domain : item.comments || '暂无描述' }}
|
||||
</sapn>
|
||||
<el-button size="small" round>获取</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-image pt-3">
|
||||
<el-image v-if="item.files" :src="`${JSON.parse(item.files)[0]}`" class="w-full h-1/2 max-h-[220px]" />
|
||||
<el-image v-else class="w-full h-[220px]" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-page-header>
|
||||
|
||||
<Pagination :total="total" @done="search" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import { Picture as IconPicture } from '@element-plus/icons-vue'
|
||||
import { ArrowLeft,View,Search } from '@element-plus/icons-vue'
|
||||
import { ElNotification as notify } from 'element-plus'
|
||||
import { useLayout, usePage} from "~/composables/configState";
|
||||
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
|
||||
import type {CmsArticleParam} from "~/api/cms/cmsArticle/model";
|
||||
import type { ComponentSize } from 'element-plus'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
import dayjs from "dayjs";
|
||||
import {getCmsNavigation, listCmsNavigation} from "~/api/cms/cmsNavigation";
|
||||
import {pageCmsArticle} from "~/api/cms/cmsArticle";
|
||||
import {pageCmsWebsiteAll} from "~/api/cms/cmsWebsite";
|
||||
import type {CmsWebsite, CmsWebsiteParam} from "~/api/cms/cmsWebsite/model";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const navId = ref();
|
||||
// 页面信息
|
||||
const list = ref<CmsWebsite[]>([]);
|
||||
const i18n = useI18n();
|
||||
const category = ref<CmsNavigation[]>([]);
|
||||
const total = ref(0);
|
||||
const activeName = ref('2839');
|
||||
const id = ref<number>();
|
||||
|
||||
// 获取状态
|
||||
const page = usePage();
|
||||
const layout = useLayout();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CmsWebsiteParam>({
|
||||
keywords: '',
|
||||
page: 1,
|
||||
limit: 12,
|
||||
status: undefined,
|
||||
recommend: undefined,
|
||||
market: true,
|
||||
websiteType: undefined,
|
||||
categoryId: undefined,
|
||||
userId: undefined,
|
||||
lang: undefined
|
||||
});
|
||||
|
||||
const types = [
|
||||
{
|
||||
value: '网站',
|
||||
label: '网站',
|
||||
},
|
||||
{
|
||||
value: '小程序',
|
||||
label: '小程序',
|
||||
},
|
||||
{
|
||||
value: 'MacOS',
|
||||
label: 'MacOS',
|
||||
},
|
||||
{
|
||||
value: 'Windows',
|
||||
label: 'Windows',
|
||||
},
|
||||
{
|
||||
value: 'App',
|
||||
label: 'App',
|
||||
},
|
||||
]
|
||||
|
||||
const goBack = () => {
|
||||
router.back();
|
||||
}
|
||||
const showDomain = (item: CmsWebsite) => {
|
||||
id.value = Number(item.websiteId);
|
||||
};
|
||||
|
||||
const hideDomain = () => {
|
||||
id.value = 0;
|
||||
};
|
||||
|
||||
// 加载页面数据
|
||||
const reload = async () => {
|
||||
await pageCmsWebsiteAll(where).then(response => {
|
||||
if(response?.list){
|
||||
list.value = response?.list;
|
||||
total.value = response.count;
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
const bakReload = async () => {
|
||||
await getCmsNavigation(navId.value).then(data => {
|
||||
page.value = data;
|
||||
layout.value.banner = data.banner;
|
||||
|
||||
// seo
|
||||
useSeoMeta({
|
||||
description: data.comments || data.title,
|
||||
keywords: data.title,
|
||||
titleTemplate: `${data?.title}` + ' - %s',
|
||||
})
|
||||
|
||||
// 二级栏目分类
|
||||
listCmsNavigation({
|
||||
parentId: data.parentId == 0 ? data.navigationId : data.parentId
|
||||
}).then(navigation => {
|
||||
category.value = navigation;
|
||||
// 加载文章列表
|
||||
if(data.parentId == 0 && category.value.length > 0){
|
||||
where.parentId = page.value.navigationId;
|
||||
}else {
|
||||
where.categoryId = page.value.navigationId;
|
||||
}
|
||||
pageCmsArticle(where).then(response => {
|
||||
if(response){
|
||||
total.value = response.count;
|
||||
list.value = response.list;
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* @param data
|
||||
*/
|
||||
const search = (data: CmsArticleParam) => {
|
||||
where.page = data.page;
|
||||
reload();
|
||||
}
|
||||
|
||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
console.log(tab, event)
|
||||
}
|
||||
const value = ref('')
|
||||
|
||||
const onTypes = (value: string) => {
|
||||
where.websiteType = value;
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(id) => {
|
||||
where.userId = getNavIdByParamsId(id);
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.right .content img{
|
||||
width: auto !important;
|
||||
}
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -13,32 +13,48 @@
|
||||
<el-card shadow="hover" class="my-10 px-2">
|
||||
<el-row :gutter="30" justify="space-between">
|
||||
<el-col :span="13">
|
||||
<el-alert title="填写您的需求,为您量身定制." type="warning" />
|
||||
<div class="my-2">
|
||||
<el-alert title="填写您的需求,为您量身定制." type="warning"/>
|
||||
</div>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120"
|
||||
label-position="left"
|
||||
class="mt-5"
|
||||
status-icon
|
||||
>
|
||||
<el-form-item :label="$t('order.title')" prop="title" class="hover:bg-gray-50 p-2">
|
||||
<el-select
|
||||
v-model="form.title"
|
||||
filterable
|
||||
placeholder="选择咨询的产品"
|
||||
placeholder="选择产品"
|
||||
@change="onWebsite"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in siteList"
|
||||
:key="item.websiteId"
|
||||
:label="item.websiteName"
|
||||
:value="`${item.websiteId}`"
|
||||
/>
|
||||
:value="item.websiteId"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ item.websiteName }}</span>
|
||||
<span class="text-gray-300">
|
||||
{{ `${item.websiteCode}.websoft.top` }}
|
||||
</span>
|
||||
</div>
|
||||
</el-option>
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="item in siteList"-->
|
||||
<!-- :key="item.websiteId"-->
|
||||
<!-- :label="item.websiteName"-->
|
||||
<!-- :value="`${item.websiteId}`"-->
|
||||
<!-- />-->
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.content')" prop="content" class="hover:bg-gray-50 p-2">
|
||||
<el-input type="textarea" :rows="5" cols="80" v-model="form.content" :placeholder="$t('order.content')"/>
|
||||
<el-input type="textarea" :rows="5" cols="80" v-model="form.content"
|
||||
placeholder="填写您的开发需求"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('order.realName')" prop="realName" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.realName" :placeholder="$t('order.realName')"/>
|
||||
@@ -49,9 +65,9 @@
|
||||
<el-form-item :label="$t('order.email')" prop="email" class="hover:bg-gray-50 p-2">
|
||||
<el-input v-model="form.email" :placeholder="$t('order.email')"/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item :label="$t('order.address')" prop="address" class="hover:bg-gray-50 p-2">-->
|
||||
<!-- <el-input v-model="form.address" :placeholder="$t('order.address')"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item :label="$t('order.address')" prop="address" class="hover:bg-gray-50 p-2">-->
|
||||
<!-- <el-input v-model="form.address" :placeholder="$t('order.address')"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item :label="$t('order.code')" prop="code" class="hover:bg-gray-50 p-2">
|
||||
<el-space class="flex">
|
||||
<el-input size="large" :placeholder="$t('order.imgCode')" maxlength="5" v-model="form.code"/>
|
||||
@@ -68,7 +84,7 @@
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-image class="py-2" v-if="page.icon" :src="page.icon" />
|
||||
<el-image class="py-2" v-if="page.icon" :src="page.icon"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
@@ -123,6 +139,8 @@ const {form, resetFields} = useFormData<CmsOrder>({
|
||||
type: undefined,
|
||||
// 关联项目ID,配合订单类型使用
|
||||
articleId: undefined,
|
||||
// 关联网站ID
|
||||
websiteId: undefined,
|
||||
// 真实姓名
|
||||
realName: undefined,
|
||||
// 手机号码
|
||||
@@ -177,7 +195,7 @@ const rules = reactive<FormRules<CmsOrder>>({
|
||||
{required: true, message: '请输入联系人姓名', trigger: 'blur'},
|
||||
],
|
||||
content: [
|
||||
{required: true, message: '请输入留言内容', trigger: 'blur'},
|
||||
{required: true, message: '请输入您的开发需求', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
@@ -196,11 +214,14 @@ const reload = async () => {
|
||||
layout.value.banner = data.banner;
|
||||
// 二级栏目分类
|
||||
pageCmsWebsiteAll({
|
||||
official: true,
|
||||
sort: 'websiteId',
|
||||
order: 'asc'
|
||||
}).then(res => {
|
||||
siteList.value = res?.list || [];
|
||||
})
|
||||
// 用户信息
|
||||
if(user.value){
|
||||
if (user.value) {
|
||||
form.realName = user.value.realName;
|
||||
form.phone = user.value.phone;
|
||||
form.email = user.value.email;
|
||||
@@ -215,6 +236,11 @@ const reload = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
const onWebsite = (item: CmsWebsite) => {
|
||||
form.articleId = item.websiteId;
|
||||
form.websiteId = item.websiteId;
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-table-column prop="createTime" label="下单时间" />
|
||||
<el-table-column prop="action" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button @click="openSpmUrl(`https://${scope.row.tenantId}.websoft.top`,form,scope.row.tenantId,true)">控制台</el-button></template>
|
||||
<el-button @click="openUrl(`https://${scope.row.tenantId}.websoft.top`,form,scope.row.tenantId,true)">控制台</el-button></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
@@ -4,25 +4,35 @@
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 用户中心 </span>
|
||||
</template>
|
||||
<template #extra>
|
||||
<nuxt-link to="/user/modify" class="text-gray-400 text-sm">修改资料</nuxt-link>
|
||||
</template>
|
||||
<div class="login-layout mt-10 sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3">
|
||||
<!-- 用户菜单 -->
|
||||
<!-- <UserMenu :activeIndex="activeIndex" @done="onDone" class="sm:flex hidden"/>-->
|
||||
<div class="flash bg-white rounded-lg w-full">
|
||||
<div class="title text-xl text-gray-700 md:px-8 p-4 md:mt-3 font-500">账号信息</div>
|
||||
<div class="lg:w-screen-lg w-full sm:px-4 sm:py-4 mb-10">
|
||||
<el-descriptions class="px-4" :column="2" border>
|
||||
<el-descriptions-item label="用户ID">{{user?.userId}}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号码">{{user?.mobile}}</el-descriptions-item>
|
||||
<el-descriptions-item label="昵称">{{user?.nickname}}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{user?.sexName}}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{user?.email}}</el-descriptions-item>
|
||||
<el-descriptions-item label="生日">{{user?.birthday}}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在省份">{{user?.province}}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在城市">{{user?.city}}</el-descriptions-item>
|
||||
<el-descriptions-item label="可用余额">{{user?.balance}}</el-descriptions-item>
|
||||
<el-descriptions-item label="可用积分">{{user?.points}}</el-descriptions-item>
|
||||
<el-descriptions-item label="个人简介">{{user?.introduction}}</el-descriptions-item>
|
||||
<div class="lg:w-screen-lg w-full sm:px-4 sm:py-4 mb-10 mt-5">
|
||||
<el-descriptions title="用户资料" :column="2" class="px-4" border>
|
||||
<el-descriptions-item
|
||||
label="头像"
|
||||
>
|
||||
<el-image
|
||||
style="width: 70px; height: 70px"
|
||||
:src="user?.avatar"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号码">{{ user?.mobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户ID">{{ user?.userId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="昵称">{{ user?.nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ user?.sexName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ user?.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在省份">{{ user?.province }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在城市">{{ user?.city }}</el-descriptions-item>
|
||||
<el-descriptions-item label="可用余额">{{ user?.balance }}</el-descriptions-item>
|
||||
<el-descriptions-item label="可用积分">{{ user?.points }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item :rowspan="1" :span="2" label="生日">{{ user?.birthday }}</el-descriptions-item>-->
|
||||
<el-descriptions-item :rowspan="1" :span="2" label="个人签名">
|
||||
<p class="min-h-[60px]">{{ user?.introduction }}</p>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,14 +42,12 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowLeft,View,Search } from '@element-plus/icons-vue'
|
||||
import {ArrowLeft, View, Search} from '@element-plus/icons-vue'
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import UserMenu from "./components/UserMenu.vue";
|
||||
import {updateUser} from "~/api/layout";
|
||||
import {openUrl} from "~/utils/common";
|
||||
|
||||
|
||||
// 配置信息
|
||||
@@ -53,19 +61,18 @@ const activeIndex = ref('');
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
nickname: undefined,
|
||||
username: undefined,
|
||||
phone: undefined,
|
||||
mobile: undefined,
|
||||
sex: undefined,
|
||||
sexName: undefined,
|
||||
email: undefined,
|
||||
password: undefined,
|
||||
code: undefined,
|
||||
smsCode: undefined,
|
||||
comments: undefined,
|
||||
remember: true,
|
||||
tenantId: undefined,
|
||||
tenantName: undefined
|
||||
});
|
||||
|
||||
@@ -82,10 +89,10 @@ const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
if(user.value){
|
||||
if (user.value) {
|
||||
form.userId = user.value.userId;
|
||||
form.nickname = user.value.nickname;
|
||||
form.realName = user.value.realName;
|
||||
@@ -100,6 +107,12 @@ const goBack = () => {
|
||||
router.back(); // 返回上一页
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
updateUser(form).then(() => {
|
||||
ElMessage.success('修改成功');
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
|
||||
177
pages/user/modify.vue
Normal file
177
pages/user/modify.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl m-auto py-4 my-20">
|
||||
<el-page-header :icon="ArrowLeft" @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large font-600 mr-3"> 修改资料 </span>
|
||||
</template>
|
||||
<div class="login-layout mt-10 sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3">
|
||||
<div class="flash bg-white rounded-lg w-full">
|
||||
<div class="lg:w-screen-lg w-full sm:px-4 sm:py-4 mb-10 mt-4">
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="7">
|
||||
<el-form-item label="上传头像" class="px-4">
|
||||
<el-upload
|
||||
v-model:file-list="avatar"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: 5,
|
||||
}"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="avatarRemove"
|
||||
:on-success="avatarSuccess"
|
||||
>
|
||||
<el-icon>
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form-item label="手机号码" class="px-4">
|
||||
<el-input disabled v-model="form.mobile"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" class="px-4">
|
||||
<el-input v-model="form.nickname"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱账号" class="px-4">
|
||||
<el-input v-model="form.email" placeholder="邮箱账号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" class="px-4">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio value="1">男</el-radio>
|
||||
<el-radio value="2">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="个人签名" class="px-4">
|
||||
<el-input v-model="form.introduction" type="textarea" placeholder="个人签名" :rows="4"/>
|
||||
</el-form-item>
|
||||
<el-form-item class="px-4">
|
||||
<el-button type="primary" class="sm:w-auto w-full" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-page-header>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {ArrowLeft, View, Search, Plus} from '@element-plus/icons-vue'
|
||||
import type {FormInstance, FormRules, UploadProps, UploadUserFile} from 'element-plus'
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import {updateUser} from "~/api/layout";
|
||||
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const tenantId = localStorage.getItem('ServerTenantId')
|
||||
const token = useToken();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const website = useWebsite()
|
||||
const user = useUser();
|
||||
const activeIndex = ref('');
|
||||
const avatar = ref<UploadUserFile[]>([])
|
||||
const dialogImageUrl = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: undefined,
|
||||
username: undefined,
|
||||
avatar: undefined,
|
||||
phone: undefined,
|
||||
mobile: undefined,
|
||||
sex: undefined,
|
||||
sexName: undefined,
|
||||
email: undefined,
|
||||
password: undefined,
|
||||
code: undefined,
|
||||
smsCode: undefined,
|
||||
comments: undefined,
|
||||
introduction: undefined,
|
||||
remember: true,
|
||||
tenantName: undefined
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
if (user.value) {
|
||||
form.userId = user.value.userId;
|
||||
form.nickname = user.value.nickname;
|
||||
form.realName = user.value.realName;
|
||||
form.avatar = user.value.avatar;
|
||||
form.mobile = user.value.mobile;
|
||||
form.email = user.value.email;
|
||||
form.sex = user.value.sex;
|
||||
form.comments = user.value.comments;
|
||||
form.introduction = user.value.introduction
|
||||
avatar.value = []
|
||||
if(form.avatar){
|
||||
avatar.value.push({
|
||||
uid: form.userId,
|
||||
url: form.avatar,
|
||||
name: '用户头像',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
router.back(); // 返回上一页
|
||||
}
|
||||
|
||||
const avatarRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.avatar = '';
|
||||
}
|
||||
|
||||
const avatarSuccess = (e: any) => {
|
||||
form.avatar = e.data.downloadUrl
|
||||
}
|
||||
|
||||
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
dialogImageUrl.value = uploadFile.url!
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
updateUser(form).then(() => {
|
||||
ElMessage.success('修改成功');
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
1647
pnpm-lock.yaml
generated
1647
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
<svg width="70" height="70" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient x1="17.621%" y1="50%" x2="100%" y2="50%" id="a"><stop stop-color="#FFE2B8" offset="0%"/><stop stop-color="#FFCA7C" offset="100%"/></linearGradient><filter x="-10.7%" y="-10.7%" width="121.4%" height="121.4%" filterUnits="objectBoundingBox" id="c"><feGaussianBlur stdDeviation="6" in="SourceAlpha" result="shadowBlurInner1"/><feOffset dx="3" in="shadowBlurInner1" result="shadowOffsetInner1"/><feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0" in="shadowInnerInner1"/></filter><path id="b" d="m1291 377 70 70v-70z"/></defs><g transform="translate(-1291 -377)" fill="none" fill-rule="evenodd"><use fill="url(#a)" xlink:href="#b"/><use fill="#000" filter="url(#c)" xlink:href="#b"/></g></svg>
|
||||
|
After Width: | Height: | Size: 953 B |
47
utils/editor.ts
Normal file
47
utils/editor.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { ref } from 'vue';
|
||||
import TinymceEditor from '@/components/TinymceEditor/index.vue';
|
||||
|
||||
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null);
|
||||
// 编辑器配置信息
|
||||
export function editorConfig(height?: 500): void {
|
||||
const resultData = ref<any>({
|
||||
height: height,
|
||||
// 自定义文件上传(这里使用把选择的文件转成 blob 演示)
|
||||
file_picker_callback: (callback: any, _value: any, meta: any) => {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
// 设定文件可选类型
|
||||
if (meta.filetype === 'image') {
|
||||
input.setAttribute('accept', 'image/*');
|
||||
} else if (meta.filetype === 'media') {
|
||||
input.setAttribute('accept', 'video/*');
|
||||
}
|
||||
input.onchange = () => {
|
||||
const file = input.files?.[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
if (meta.filetype === 'media') {
|
||||
if (!file.type.startsWith('video/')) {
|
||||
editorRef.value?.alert({ content: '只能选择视频文件' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
editorRef.value?.alert({ content: '大小不能超过 20MB' });
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (e.target?.result != null) {
|
||||
const blob = new Blob([e.target.result], { type: file.type });
|
||||
callback(URL.createObjectURL(blob));
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
});
|
||||
return resultData;
|
||||
}
|
||||
Reference in New Issue
Block a user