Compare commits

...

19 Commits
master ... min

  1. 3
      .env.development
  2. 3
      .env.production
  3. 1
      .env.test
  4. 748
      package-lock.json
  5. 3
      package.json
  6. 16
      src/App.vue
  7. 30
      src/apis/business.js
  8. 4
      src/apis/index.js
  9. 21
      src/apis/plugin.js
  10. BIN
      src/assets/ck-logo.png
  11. BIN
      src/assets/iPhone13.png
  12. BIN
      src/assets/pic-bg.png
  13. 19
      src/components/breadcrumb.vue
  14. 81
      src/components/configure.vue
  15. 45
      src/components/leftMenu.vue
  16. 215
      src/components/listPlugin.vue
  17. 54
      src/components/listSearchBar.vue
  18. 117
      src/components/listTable.vue
  19. 77
      src/components/navbar.vue
  20. 69
      src/components/pagination.vue
  21. 85
      src/components/plugin.vue
  22. 113
      src/components/pluginListTable.vue
  23. 71
      src/components/relevance.vue
  24. 136
      src/components/searchBar.vue
  25. 55
      src/routers/index.js
  26. 17
      src/store/index.js
  27. 26
      src/store/plugin.js
  28. 3
      src/utils/axios.js
  29. 18
      src/utils/time.js
  30. 33
      src/views/Index.vue
  31. 140
      src/views/index-list/add-business.vue
  32. 350
      src/views/index-list/add-plugin.vue
  33. 194
      src/views/index-list/business-detail.vue
  34. 79
      src/views/index-list/business-list.vue
  35. 14
      src/views/index-list/console-desk.vue
  36. 82
      src/views/index-list/plugin-list.vue
  37. 67
      src/views/index-list/plugin-shop.vue
  38. 2
      src/views/user/pw-change.vue
  39. 2
      src/views/user/sign-in.vue
  40. 2
      vite.config.js
  41. 166
      yarn.lock

3
.env.development

@ -1 +1,2 @@
VITE_API_URL=http://localhost:4001 VITE_API_URL=https://test.tall.wiki
VITE_API_URL_NEW=http://101.201.226.163

3
.env.production

@ -1 +1,2 @@
VITE_API_URL=http://139.196.27.233:29001 VITE_API_URL=http://www.tall.wiki
VITE_API_URL_NEW=http://101.201.226.163

1
.env.test

@ -1 +1,2 @@
VITE_API_URL=https://test.tall.wiki VITE_API_URL=https://test.tall.wiki
VITE_API_URL_NEW=http://101.201.226.163

748
package-lock.json

File diff suppressed because it is too large

3
package.json

@ -25,6 +25,7 @@
"vite-plugin-compression": "^0.3.5", "vite-plugin-compression": "^0.3.5",
"vite-plugin-windicss": "^1.4.11", "vite-plugin-windicss": "^1.4.11",
"vue": "^3.2.16", "vue": "^3.2.16",
"vue-clipboard3": "^1.0.1",
"vue-router": "^4.0.12", "vue-router": "^4.0.12",
"vuex": "^4.0.2", "vuex": "^4.0.2",
"windicss": "^3.1.9" "windicss": "^3.1.9"
@ -40,7 +41,7 @@
"eslint-plugin-html": "^6.2.0", "eslint-plugin-html": "^6.2.0",
"eslint-plugin-import": "^2.25.2", "eslint-plugin-import": "^2.25.2",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^7.19.1", "eslint-plugin-vue": "^7.20.0",
"husky": "^7.0.2", "husky": "^7.0.2",
"lint-staged": "^11.2.3", "lint-staged": "^11.2.3",
"prettier": "^2.4.1", "prettier": "^2.4.1",

16
src/App.vue

@ -1,5 +1,5 @@
<template> <template>
<el-config-provider :locale="local"> <el-config-provider>
<el-container> <el-container>
<router-view></router-view> <router-view></router-view>
</el-container> </el-container>
@ -20,6 +20,20 @@ if (userString) {
} else { } else {
router.push({ name: 'signin' }); router.push({ name: 'signin' });
} }
//
// function getSystemInfo() {
// uni.getSystemInfo({
// success: result => {
// this.$store.commit('setSystemInfo', result);
// },
// fail: error => {
// console.error('getSystemInfo fail:', error);
// },
// });
// }
// getSystemInfo()
</script> </script>
<style> <style>

30
src/apis/business.js

@ -0,0 +1,30 @@
// noinspection SpellCheckingInspection
import http from 'utils/axios';
const apiUrl = import.meta.env.VITE_API_URL_NEW;
const business = `${apiUrl}/gateway/opt/business`;
// 查询业务列表
export const queryBusiness = params => http.post(`${business}/query`, params);
// 查询业务下关联的插件
export const queryPluginByBusiness = params => http.post(`${business}/queryPlugin`, params);
// 关联业务和插件
export const relevance = params => http.post(`${business}/relevance`, params);
// 添加业务信息
export const saveBusiness = params => http.post(`${business}/save`, params);
// 修改业务下的插件配置信息
export const updateConfig = params => http.post(`${business}/updateConfig`, params);
// 通过id查询业务信息
export const queryIdBusiness = params => http.post(`${business}/queryIdBusiness`, params);
// 移除业务下关联的插件
export const delRelevance = params => http.post(`${business}/delRelevance`, params);
// 删除业务
export const delBusiness = params => http.post(`${business}/del`, params);

4
src/apis/index.js

@ -2,8 +2,8 @@
import http from 'utils/axios'; import http from 'utils/axios';
const apiUrl = import.meta.env.VITE_API_URL; const apiUrl = import.meta.env.VITE_API_URL_NEW;
const users = `${apiUrl}/gateway/tall3/v3.0/users`; const users = `${apiUrl}/gateway/ptostall/users`;
// 根据userId 获取token // 根据userId 获取token
export const getToken = userId => http.get(`${users}/userId`, { params: { userId } }); export const getToken = userId => http.get(`${users}/userId`, { params: { userId } });

21
src/apis/plugin.js

@ -0,0 +1,21 @@
// noinspection SpellCheckingInspection
import http from 'utils/axios';
const apiUrl = import.meta.env.VITE_API_URL_NEW;
const plugin = `${apiUrl}/gateway/opt/plugin`;
// 查询插件列表
export const queryPlugins = params => http.post(`${plugin}/query`, params);
// 获取一个随机id
export const randomId = params => http.post(`${plugin}/randomId`, params);
// 创建插件
export const savePlugin = params => http.post(`${plugin}/save`, params);
// 修改插件信息
export const updatePlugin = params => http.post(`${plugin}/update`, params);
// 删除插件
export const delPlugin = params => http.post(`${plugin}/del`, params);

BIN
src/assets/ck-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/iPhone13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
src/assets/pic-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

19
src/components/breadcrumb.vue

@ -0,0 +1,19 @@
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item, index) in path" :key="index">
<span @click="router.push({ name: item.name })" v-if="index !== path.length - 1" class="font-bold cursor-pointer">{{
item.title
}}</span>
<span v-else>{{ item.title }}</span>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup>
import { useRouter } from 'vue-router';
import { defineProps } from 'vue';
const router = useRouter();
defineProps({ path: { default: () => [], type: Array } });
</script>

81
src/components/configure.vue

@ -0,0 +1,81 @@
<template>
<el-button type="primary" @click="data.dialogVisible = true">配置</el-button>
<el-dialog v-model="data.dialogVisible" title="配置" width="40%">
<!-- main -->
<el-form ref="formRef" :model="form" :rules="rules" label-width="150px" class="forms">
<el-form-item label="配置文件:" prop="config">
<el-input v-model="form.config" type="textarea" placeholder="请输入配置文件"></el-input>
</el-form-item>
<el-form-item> <el-switch v-model="form.debug" class="mr-3"></el-switch> 是否开启debug模式 </el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit()">发布</el-button>
<el-button @click="resetForm()">重置</el-button>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script setup>
import { defineProps, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { updateConfig } from '@/apis/business';
const formRef = ref(null);
const props = defineProps({
businessPluginId: { default: '', type: String },
oldConfig: { default: '', type: String },
oldDebug: { default: 0, type: Number },
});
const form = reactive({
config: '',
debug: false,
});
const rules = { config: [{ required: true, message: '请输入配置文件', trigger: 'blur' }] };
const data = reactive({ dialogVisible: false });
if (props.oldConfig) {
form.config = props.oldConfig;
}
if (props.oldDebug) {
form.debug = props.oldDebug === 1;
}
/**
* 配置修改
* @param {object} params
*/
function onSubmit() {
try {
formRef.value.validate(async valid => {
if (valid) {
const params = {
param: {
businessPluginId: props.businessPluginId,
config: form.config,
debug: form.debug ? 1 : 0,
},
};
await updateConfig(params);
ElMessage.success('配置修改成功');
resetForm();
data.dialogVisible = true;
} else {
console.log('error submit!!');
return false;
}
});
} catch (error) {
ElMessage.error(error || '配置修改失败');
console.error('error: ', error);
}
}
//
function resetForm() {
formRef.value.resetFields();
}
</script>

45
src/components/leftMenu.vue

@ -0,0 +1,45 @@
<template>
<el-menu :default-active="data.menuIndex" class="el-menu-demo" mode="vertical" @select="handleSelect">
<el-menu-item :disabled="list.disabled" :index="index" @click="openPage(list.name)" v-for="(list, index) in data.menus" :key="index">
{{ list.title }}
</el-menu-item>
</el-menu>
</template>
<script setup>
import { useStore } from 'vuex';
import { reactive, computed, watchEffect } from 'vue';
import { useRouter } from 'vue-router';
const store = useStore();
const leftIndex = computed(() => store.state.plugin.leftIndex);
const router = useRouter();
const data = reactive({
menus: [
{ title: '概览', name: '', disabled: true },
{ title: '创建业务', name: 'desk-add-business', disabled: false },
{ title: '创建插件', name: 'desk-add-plugin', disabled: false },
{ title: '我的业务', name: 'desk-business-list', disabled: false },
{ title: '我的插件', name: 'desk-plugin-list', disabled: false },
],
menuIndex: '',
});
console.log('menuIndex: ', data.menuIndex);
watchEffect(() => {
if (leftIndex && leftIndex.value) {
data.menuIndex = leftIndex.value;
console.log('data.menuIndex: ', data.menuIndex);
}
});
function handleSelect(e) {
store.commit('plugin/setLeftIndex', e);
}
//
function openPage(name) {
router.push({ name });
}
</script>

215
src/components/listPlugin.vue

@ -0,0 +1,215 @@
<template>
<div v-if="data.listArray && data.listArray.length">
<div class="shop-content flex py-5" v-for="(list, index) in data.listArray" :key="list.id">
<img :src="list.preview" alt="" class="shop-left" />
<div class="shop-right ml-8">
<div>
<span class="text-xl font-semibold">{{ list.name }}</span> <span class="ml-5 text-sm">{{ list.versions }}</span>
</div>
<div class="mt-4 desc text-sm">
<span> 描述</span> <span>{{ list.intro }}</span>
</div>
<div class="mt-5 text-sm" v-if="list.tags && list.tags.length">
<el-tag v-for="item in list.tags" :type="item.btnType" class="mr-3" :key="item.name">{{ item.name }}</el-tag>
</div>
<div class="mt-5 text-sm flex flex-row">
<span>行业:</span> <span>{{ list.industryName || '无' }}</span> <span class="ml-8">分类:</span>
<span>{{ list.sortName || '无' }}</span>
</div>
<div class="mt-4 text-sm">
<span>更新日期:</span> <span>{{ list.updateTime || '无' }}</span> <span class="ml-8">作者:</span>
<span>{{ list.authorName || '无' }}</span>
</div>
<div class="mt-6">
<el-popconfirm
title="确定要移除该插件吗?"
confirm-button-text="确定"
cancel-button-text="再想想"
v-if="showConfig"
@confirm="deleteRelevance(list)"
>
<template #reference>
<el-button type="primary">移除</el-button>
</template>
</el-popconfirm>
<el-button type="primary" disabled>下载源代码</el-button>
<el-button type="primary" disabled>下载示例代码</el-button>
<!-- 添加到业务弹框 -->
<el-popover placement="right" title="请选择业务" :width="240" trigger="click" v-if="!showConfig">
<div class="radio-box">
<Relevance
:businessLists="data.businessLists"
:pluginId="list.id"
:isLastPage="data.isLastPage"
@changePageNum="changePageNum"
/>
</div>
<template #reference>
<el-button type="primary" @click="handleQueryBusiness">添加到业务</el-button>
</template>
</el-popover>
<!-- 配置 -->
<!-- <el-button type="primary" v-if="showConfig" @click="data.dialogVisible = true">配置</el-button> -->
<Configure v-if="showConfig" :businessPluginId="list.businessPluginId" :oldConfig="list.config" :oldDebug="list.debug" />
<!-- 预览 -->
<el-button type="primary" @click="collapseVisible(index)" style="margin-left: 10px">{{
!list.visible ? '预览' : '关闭预览'
}}</el-button>
<div class="plugin-box" v-if="list.visible">
<div class="plugin w-full h-full overflow-y-scroll">
<Plugin :plugin="list" />
</div>
</div>
</div>
</div>
</div>
</div>
<el-empty v-else description="暂无插件信息"></el-empty>
</template>
<script lang="ts" setup="true">
import { defineProps, reactive, watchEffect, defineEmits } from 'vue';
import { ElMessage } from 'element-plus';
import { queryBusiness, delRelevance } from '@/apis/business.js';
const data = reactive({
currentPage: 1,
businessLists: [],
isLastPage: false,
listArray: [],
});
const props = defineProps({
lists: { default: () => [], type: Array },
showConfig: { default: false, type: Boolean },
businessId: { default: '', type: String },
});
const emit = defineEmits(['queryPluginOfBusiness']);
watchEffect(() => {
if (props.lists && props.lists.length) {
data.listArray = [];
props.lists.forEach(item => {
item.visible = false;
data.listArray.push(item);
});
}
});
//
function changePageNum() {
data.currentPage++;
handleQueryBusiness();
}
/**
* 查询业务列表
* @param {number} depth 查询深度 0则只查名称1则查询全部
* @param {number} name 插件名称为空则不实用该条件
* @param {number} pageNum 第几页
* @param {number} pageSize 每页几条信息
*/
async function handleQueryBusiness() {
try {
const { currentPage } = data;
if (currentPage === 1) {
data.businessLists = [];
}
const params = {
param: {
depth: 0,
name: '',
pageNum: currentPage,
pageSize: 10,
},
};
const res = await queryBusiness(params);
if (res.list.length) {
res.list.forEach(item => {
data.businessLists.push(item);
});
}
data.currentPage = res.pageNum - 0;
data.isLastPage = res.isLastPage;
} catch (error) {
console.error('error: ', error);
}
}
//
function collapseVisible(index) {
data.listArray.forEach((item, i) => {
if (i === index) {
item.visible = !item.visible;
} else {
item.visible = false;
}
});
}
/**
* 移除业务下的插件
*/
async function deleteRelevance(list) {
try {
const params = { param: { businessPluginId: list.businessPluginId } };
await delRelevance(params);
ElMessage.success('删除成功');
emit('queryPluginOfBusiness', props.businessId);
} catch (error) {
ElMessage.success(error || '删除失败');
}
}
</script>
<style scoped>
.el-input {
width: 20%;
margin-right: 2rem;
}
.el-select {
margin-right: 2rem;
width: 20%;
}
.desc {
height: 30px;
line-height: 15px;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
.shop-left {
width: 288px;
height: 263px;
}
.radio-box {
width: 100%;
}
.radio-box >>> .el-radio {
min-width: 100%;
margin: 0 !important;
}
.plugin-box {
position: fixed;
right: 14rem;
background: url(../assets/iPhone13.png) no-repeat;
background-size: contain;
width: 19.375rem;
height: 39.2419rem;
top: calc(50vh - 289.93512px + 1.8rem);
padding: 2.8rem 1.2rem 2rem;
z-index: 99;
}
.plugin::-webkit-scrollbar {
display: none;
}
</style>

54
src/components/listSearchBar.vue

@ -0,0 +1,54 @@
<template>
<div class="flex flex-row justify-space-between items-center">
<div class="flex flex-row items-center">
<div>业务名称</div>
<el-input disabled v-model="data.keywords" placeholder="请输入业务名称或标签搜索" class="search-input mr-10" />
<div>状态</div>
<el-select disabled v-model="data.status" placeholder="请选择" class="search-input" @change="changeState">
<el-option v-for="item in data.statusList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
</div>
<div class="flex-1 ml-10">
<el-button type="primary" size="small" class="w-60px h-40px" disabled>查询</el-button>
<el-button size="small" class="w-60px h-40px reset" disabled>重置</el-button>
</div>
</div>
</template>
<script lang="ts" setup="true">
import { reactive } from 'vue';
const data = reactive({
keywords: '',
status: '',
statusList: [
{
value: '状态一',
label: '状态一',
},
{
value: '状态二',
label: '状态二',
},
{
value: '状态三',
label: '状态三',
},
],
});
function changeState(e) {
console.log('e: ', e);
data.status = e;
}
</script>
<style scoped>
.search-input {
width: 18rem;
}
.reset {
margin-left: 0.75rem;
}
</style>

117
src/components/listTable.vue

@ -0,0 +1,117 @@
<template>
<div v-if="lists && lists.length">
<el-table :data="lists" class="bg-title" style="width: 100%">
<el-table-column prop="name" label="业务名称"> </el-table-column>
<el-table-column prop="appId" label="APPID" width="300"> </el-table-column>
<el-table-column prop="startUsing" label="状态" key="slot" sortable>
<template #default="scope">
<div class="flex flex-row items-center" @click="change(scope.row)">
<div :class="scope.row.startUsing ? 'point bg-green-500' : 'point bg-red-500'"></div>
<span style="margin-left: 10px">{{ scope.row.startUsing ? '启动' : '禁用' }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="公开" sortable key="slot" width="180">
<template #default="scope">
<div class="flex flex-row items-center" @click="change(scope.row)">
<div :class="scope.row.pub ? 'point bg-green-500' : 'point bg-red-500'"></div>
<span style="margin-left: 10px">{{ scope.row.pub ? '公开' : '非公开' }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" :formatter="changeDate" label="创建日期" sortable></el-table-column>
<el-table-column label="操作" key="slot">
<template #default="scope">
<el-popconfirm
title="确定删除这条业务吗?"
confirm-button-text="确定"
cancel-button-text="再想想"
@confirm="deleteBusiness(scope.row)"
>
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
<el-button type="text" size="small" disabled>配置</el-button>..
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
</template>
</el-table-column>
<template slot="empty">
<p>没有记录哦~</p>
</template>
</el-table>
</div>
<el-empty v-else description="暂无业务信息"></el-empty>
</template>
<script setup>
import { useRouter } from 'vue-router';
import { defineProps, defineEmits } from 'vue';
import { useStore } from 'vuex';
import time from 'utils/time';
import { ElMessage } from 'element-plus';
import { delBusiness } from '@/apis/business.js';
const router = useRouter();
const store = useStore();
defineProps({
lists: { default: () => [], type: Array },
currentPage: { default: 1, type: Number },
pageSize: { default: 10, type: Number },
total: { default: 0, type: Number },
showConfig: { default: false, type: Boolean },
});
const emit = defineEmits(['handleQueryBusiness']);
function change(row) {
console.log('row: ', row);
}
function handleClick(row) {
router.push({ name: 'business-detail', query: { id: row.id } });
store.commit('plugin/setBusinessInfo', row);
}
function changeDate(row) {
const value = row.createTime;
return time.dateFormat(value);
}
/**
* 删除业务
* @param {String} businessId
*/
async function deleteBusiness(row) {
try {
// async await
const params = { param: { businessId: row.id } };
await delBusiness(params);
emit('handleQueryBusiness');
ElMessage.success('删除成功');
} catch (error) {
ElMessage.error(error || '删除失败');
console.error('error: ', error);
}
}
</script>
<style scoped>
.bg-title >>> thead tr th {
color: #333;
background: #fafafa;
border-top: 1px solid #e8e8e8;
}
.bg-title >>> thead tr th:first-child {
border-left: 1px solid #e8e8e8;
}
.bg-title >>> thead tr th:last-child {
border-right: 1px solid #e8e8e8;
}
.point {
width: 6px;
height: 6px;
border-radius: 50%;
}
</style>

77
src/components/navbar.vue

@ -1,17 +1,27 @@
<template> <template>
<div class="flex items-center justify-between pr-5 shadow-sm"> <div class="flex items-center justify-between pr-5 shadow-sm">
<h1 class="text-lg font-medium py-3 px-6"> <div class="flex flex-nowrap items-center">
<i <img src="@/assets/ck-logo.png" class="w-15" />
v-if="menu.show" <h1 class="py-3 pl-2 pr-6 logo-title">开放平台</h1>
:class="{ 'text-gray-800': !menu.collapse, 'text-gray-400': menu.collapse }" </div>
class="el-icon-guide mr-2" <el-menu
@click="toggleCollapse" class="menu flex-1"
></i> background-color="#00182E"
智能大气腐蚀监测平台{{ $route.meta.title }} text-color="#A5ADB5"
</h1> active-text-color="#fff"
:default-active="data.activeIndex"
mode="horizontal"
>
<el-menu-item :index="index" @click="openPage(list.name)" v-for="(list, index) in data.lists" :key="index">
{{ list.title }}
</el-menu-item>
</el-menu>
<div class="flex items-center" style="color: #a5adb5">
<div @click="openPage('desk-business-list', 3)" class="text-sm cursor-pointer">控制台</div>
<el-icon class="mx-6" size="16"><bell /></el-icon>
</div>
<el-dropdown> <el-dropdown>
<span class="flex items-center"> <span class="flex items-center" style="color: #a5adb5">
<el-avatar class="mr-2" size="small" src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"></el-avatar> <el-avatar class="mr-2" size="small" src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"></el-avatar>
<div class="mr-3">{{ account }}</div> <div class="mr-3">{{ account }}</div>
<el-icon class="el-icon--right"> <el-icon class="el-icon--right">
@ -20,6 +30,10 @@
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item disabled>个人资料</el-dropdown-item>
<el-dropdown-item @click="openPage('desk-business-list', 3)">我的业务</el-dropdown-item>
<el-dropdown-item @click="openPage('desk-plugin-list', 4)">我的插件</el-dropdown-item>
<el-dropdown-item disabled>实名认证</el-dropdown-item>
<el-dropdown-item @click="openChangePassword">修改密码</el-dropdown-item> <el-dropdown-item @click="openChangePassword">修改密码</el-dropdown-item>
<el-dropdown-item @click="signOut">退出登录</el-dropdown-item> <el-dropdown-item @click="signOut">退出登录</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
@ -29,20 +43,35 @@
</template> </template>
<script setup> <script setup>
import { computed } from 'vue'; import { computed, reactive } from 'vue';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { ArrowDown } from '@element-plus/icons'; import { ArrowDown, Bell } from '@element-plus/icons';
const store = useStore(); const store = useStore();
const router = useRouter(); const router = useRouter();
const toggleCollapse = () => {
console.log('Toggle Collapse');
store.commit('toggleCollapse');
};
const menu = computed(() => store.state.menu);
const account = computed(() => store.getters['user/account']); const account = computed(() => store.getters['user/account']);
const data = reactive({
lists: [
{ title: '首页', name: '' },
{ title: '解决方案', name: '' },
{ title: '案例中心', name: '' },
{ title: '插件商城', name: 'plugin-shop' },
{ title: '文档中心', name: '' },
{ title: '下载', name: '' },
],
activeIndex: 3,
});
//
function openPage(name, index) {
router.push({ name });
if (index !== '') {
store.commit('plugin/setLeftIndex', index);
}
}
// //
function openChangePassword() { function openChangePassword() {
router.push({ name: 'pw-change' }); router.push({ name: 'pw-change' });
@ -54,3 +83,15 @@ function signOut() {
router.push({ name: 'signin' }); router.push({ name: 'signin' });
} }
</script> </script>
<style scoped>
.menu {
width: 38.5rem;
}
.logo-title {
color: #dee2e6;
font-weight: 700;
font-size: 1.125rem;
}
</style>

69
src/components/pagination.vue

@ -0,0 +1,69 @@
<template>
<div class="pagination mt-15 flex flex-row-reverse">
<el-config-provider :locale="zhCn">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-size="pageSize"
layout="prev, pager, next, sizes, jumper"
:total="total"
:page-sizes="[10, 15, 20, 30, 40, 50]"
>
</el-pagination>
</el-config-provider>
</div>
</template>
<script lang="ts" setup="true">
import { defineProps, defineEmits } from 'vue';
import { ElConfigProvider } from 'element-plus';
import zhCn from 'element-plus/lib/locale/lang/zh-cn';
defineProps({
currentPage: { default: 1, type: Number },
pageSize: { default: 10, type: Number },
total: { default: 0, type: Number },
});
const emit = defineEmits(['handleSizeChange', 'handleCurrentChange']);
function handleSizeChange(val) {
emit('handleSizeChange', val);
}
function handleCurrentChange(val) {
emit('handleCurrentChange', val);
}
</script>
<style scoped>
.pagination >>> .el-pager li,
.pagination >>> .btn-prev,
.pagination >>> .btn-next {
border: 1px solid #ccc;
width: 2rem;
height: 2rem;
line-height: 2rem;
text-align: center;
padding: 0;
margin: 0 8px;
border-radius: 6px;
font-weight: normal;
}
.pagination >>> .el-pager li.active {
border: 1px solid #409eff;
color: #fff;
background: #409eff;
}
.pagination >>> .el-input__inner {
height: 2rem;
line-height: 2rem;
border: 1px solid #ccc;
border-radius: 6px !important;
}
.pagination >>> .el-pagination__jump {
margin-left: 0;
}
</style>

85
src/components/plugin.vue

@ -0,0 +1,85 @@
<template>
<div class="u-font-14" style="height: 100%">
<div v-if="data.pluginContent">
<div :data-token="token" :data-uid="userId" style="height: 100%" v-html="data.pluginContent"></div>
</div>
</div>
</template>
<script setup>
import { computed, reactive, nextTick, defineProps } from 'vue';
import { useStore } from 'vuex';
const props = defineProps({ plugin: { default: () => {}, type: Object } });
const store = useStore();
const token = computed(() => store.state.user.token);
const userId = computed(() => store.state.user.userId);
const data = reactive({ pluginContent: null });
setPlugin();
//
function setPlugin() {
let id = '';
if (props.plugin) {
if (props.plugin.id) {
id = props.plugin.id;
}
if (props.plugin.pluginId) {
id = props.plugin.pluginId;
}
}
if (!props.plugin || !id) {
previewAddPlugin();
return;
}
const reg = /data-root=["|']?(\w+)["|']?/gi;
let uuid = '';
// FIXME: js, html
if (props.plugin.html) {
// data-root=xxx xxx pluginTaskId
if (reg.test(props.plugin.html)) {
uuid = RegExp.$1;
const str = props.plugin.html.replace(new RegExp(uuid, 'g'), `p${id}`);
data.pluginContent = str;
} else {
data.pluginContent = props.plugin.html;
}
const str = props.plugin.js.replace(new RegExp(uuid, 'g'), `p${id}`);
handleDom(str, id);
}
}
// script dom
function handleDom(js, id) {
const domList = Array.from(document.getElementsByTagName('script'));
const index = domList.findIndex(item => item.id === `p${id}`);
if (index >= 0) {
document.body.removeChild(document.getElementById(`p${id}`));
}
const scriptDom = document.createElement('script');
scriptDom.id = `p${id}`;
scriptDom.setAttribute('data-type', 'plugin');
scriptDom.innerHTML = js;
nextTick(() => {
document.body.append(scriptDom);
});
}
// id
function previewAddPlugin() {
console.log('props.plugin: ', props.plugin);
// FIXME: js, html
if (props.plugin.html) {
// data-root=xxx xxx pluginTaskId
data.pluginContent = props.plugin.html;
const scriptDom = document.createElement('script');
scriptDom.setAttribute('data-type', 'plugin');
scriptDom.innerHTML = props.plugin.js;
console.log('scriptDom: ', scriptDom);
nextTick(() => {
document.body.append(scriptDom);
});
}
}
</script>

113
src/components/pluginListTable.vue

@ -0,0 +1,113 @@
<template>
<div v-if="lists && lists.length">
<el-table :data="lists" class="bg-title" style="width: 100%">
<el-table-column prop="name" label="插件名称"> </el-table-column>
<el-table-column prop="appId" label="APPID"> </el-table-column>
<el-table-column prop="startUsing" label="状态" key="slot" sortable>
<template #default="scope">
<div class="flex flex-row items-center" @click="change(scope.row)">
<div :class="!scope.row.startUsing ? 'point bg-green-500' : 'point bg-red-500'"></div>
<span style="margin-left: 10px">{{ !scope.row.startUsing ? '启动' : '禁用' }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="pub" label="公开" sortable key="slot">
<template #default="scope">
<div class="flex flex-row items-center" @click="change(scope.row)">
<div :class="!scope.row.pub ? 'point bg-green-500' : 'point bg-red-500'"></div>
<span style="margin-left: 10px">{{ !scope.row.pub ? '公开' : '非公开' }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" :formatter="changeCreateDate" label="创建日期" sortable></el-table-column>
<el-table-column prop="updateTime" :formatter="changeUpdateDate" label="更新日期" sortable></el-table-column>
<el-table-column label="操作" key="slot">
<template #default="scope">
<el-popconfirm
title="确定删除这条业务吗?"
confirm-button-text="确定"
cancel-button-text="再想想"
@confirm="deletePlugin(scope.row)"
>
<template #reference>
<el-button type="text" size="small">删除</el-button>
</template>
</el-popconfirm>
<el-button type="text" size="small" disabled>配置</el-button>..
<el-button type="text" size="small" disabled>查看</el-button>
</template>
</el-table-column>
<template slot="empty">
<p>没有记录哦~</p>
</template>
</el-table>
</div>
<el-empty v-else description="暂无业务信息"></el-empty>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
import time from 'utils/time';
import { ElMessage } from 'element-plus';
import { delPlugin } from '@/apis/plugin.js';
defineProps({
lists: { default: () => [], type: Array },
currentPage: { default: 1, type: Number },
pageSize: { default: 10, type: Number },
total: { default: 0, type: Number },
showConfig: { default: false, type: Boolean },
});
const emit = defineEmits(['handleQueryPlugins']);
function change(row) {
console.log('row: ', row);
}
function changeCreateDate(row) {
const value = row && row.createTime ? row.createTime : '';
return time.dateFormat(value);
}
function changeUpdateDate(row) {
const value = row && row.updateTime ? row.updateTime : '';
return time.dateFormat(value);
}
/**
* 删除插件
* @param {String} businessId
*/
async function deletePlugin(row) {
try {
const params = { param: { id: row.id } };
await delPlugin(params);
ElMessage.success('删除成功');
emit('handleQueryPlugins');
} catch (error) {
console.error('error: ', error);
ElMessage.error(error || '删除失败');
}
}
</script>
<style scoped>
.bg-title >>> thead tr th {
color: #333;
background: #fafafa;
border-top: 1px solid #e8e8e8;
}
.bg-title >>> thead tr th:first-child {
border-left: 1px solid #e8e8e8;
}
.bg-title >>> thead tr th:last-child {
border-right: 1px solid #e8e8e8;
}
.point {
width: 6px;
height: 6px;
border-radius: 50%;
}
</style>

71
src/components/relevance.vue

@ -0,0 +1,71 @@
<template>
<el-scrollbar height="200px" @scroll="scroll">
<el-radio @change="chooseBusiness" v-model="data.isCollapse" :label="business.id" v-for="business in businessLists" :key="business.id">
{{ business.name }}
</el-radio>
<div class="loading my-2" v-if="!businessLists.length">暂无数据</div>
<div class="loading my-2" v-else>{{ isLastPage ? '到底啦~' : '加载中...' }}</div>
</el-scrollbar>
</template>
<script setup>
import { reactive, defineProps, defineEmits } from 'vue';
import { ElMessage } from 'element-plus';
import { relevance } from '@/apis/business.js';
const props = defineProps({
businessLists: { default: () => [], type: Array },
pluginId: { default: '', type: String },
businessId: { default: '', type: String },
isLastPage: { default: false, type: Boolean },
});
const emit = defineEmits(['changePageNum', 'query']);
const data = reactive({
isCollapse: '',
showLoading: false,
scrollTop: 0,
});
function scroll(e) {
if (props.isLastPage) return;
data.scrollTop = e.scrollTop;
if (e.scrollTop === 220) {
emit('changePageNum');
}
}
/**
* 关联业务和插件
* @param {string} businessId 业务id
* @param {string} pluginId 插件id
*/
async function chooseBusiness(e) {
try {
data.isCollapse = e;
const params = {
param: {
businessId: props.businessId ? props.businessId : e,
pluginId: props.pluginId ? props.pluginId : e,
},
};
await relevance(params);
ElMessage.success('关联成功');
if (props.businessId) {
emit('query', props.businessId);
}
} catch (error) {
ElMessage.error(error || '关联失败');
console.error('error: ', error);
}
}
</script>
<style scoped>
.loading {
width: 100%;
text-align: center;
color: #aaa;
}
</style>

136
src/components/searchBar.vue

@ -0,0 +1,136 @@
<template>
<!-- 热门插件 -->
<div class="flex flex-row justify-space-between items-center">
<div class="font-bold">
热门插件
<el-button plain size="mini" v-for="(tag, tagIndex) in data.tags" :key="tagIndex" class="cursor-pointer">{{ tag }}</el-button>
</div>
<div class="flex-1"></div>
<div class="flex flex-row">
<el-input disabled v-model="data.keywords" placeholder="请输入插件名或标签名搜索" class="search-input" />
<el-button type="primary" size="small" class="w-60px h-40px" disabled>搜索</el-button>
<el-button type="primary" icon="el-icon-plus" size="small" class="h-40px" @click="openPage">创建插件</el-button>
</div>
</div>
<!-- 所属行业 -->
<div class="types">
所属行业:
<div
class="type"
:class="industry.checked ? 'check' : ''"
v-for="(industry, industryIndex) in data.industryList"
:key="industryIndex"
@click="changeIndustryCheck(industryIndex)"
>
{{ industry.value }}
</div>
</div>
<div class="line"></div>
<div class="types">
所属分类:
<div
class="type"
:class="sort.checked ? 'check' : ''"
v-for="(sort, sortIndex) in data.sortList"
:key="sortIndex"
@click="changeSortCheck(sortIndex)"
>
{{ sort.value }}
</div>
</div>
<div class="line"></div>
</template>
<script lang="ts" setup="true">
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
const router = useRouter();
const store = useStore();
const data = reactive({
tags: [],
// tags: ['', '', '', ''],
keywords: '',
industryList: [
// {
// value: '',
// label: '',
// checked: true,
// },
// {
// value: '',
// label: '',
// checked: false,
// },
// {
// value: '',
// label: '',
// checked: false,
// }
],
sortList: [
// {
// value: '',
// label: '',
// checked: false,
// },
// {
// value: '',
// label: '',
// checked: true,
// },
// {
// value: '',
// label: '',
// checked: false,
// },
],
});
function changeIndustryCheck(index) {
data.industryList[index].checked = !data.industryList[index].checked;
}
function changeSortCheck(index) {
data.sortList[index].checked = !data.sortList[index].checked;
}
function openPage() {
router.push({ name: 'desk-add-plugin' });
store.commit('plugin/setLeftIndex', 2);
}
</script>
<style scoped>
.search-input {
width: 27.3125rem;
}
.types {
display: flex;
flex-wrap: wrap;
align-items: center;
margin: 8px 0;
}
.types .type {
height: 2rem;
line-height: 2rem;
cursor: pointer;
padding: 0 12px;
margin: 6px 20px;
}
.types .check {
color: #fff;
background: #409eff;
border-radius: 3px;
}
.line {
width: 100%;
border-bottom: 1px dashed #ebebeb;
}
</style>

55
src/routers/index.js

@ -3,7 +3,7 @@ import { createRouter, createWebHistory } from 'vue-router';
export const routes = [ export const routes = [
// { // {
// path: '/store/overview', // path: '/open/overview',
// name: 'overview', // name: 'overview',
// meta: { // meta: {
// title: '设备概览', // title: '设备概览',
@ -15,13 +15,13 @@ export const routes = [
export const user = [ export const user = [
{ {
path: '/store/user/signin', path: '/open/user/signin',
name: 'signin', name: 'signin',
meta: {}, meta: {},
component: () => import('views/user/sign-in.vue'), component: () => import('views/user/sign-in.vue'),
}, },
{ {
path: '/store/user/pw-change', path: '/open/user/pw-change',
name: 'pw-change', name: 'pw-change',
meta: {}, meta: {},
component: () => import('views/user/pw-change.vue'), component: () => import('views/user/pw-change.vue'),
@ -33,18 +33,57 @@ const router = createRouter({
routes: [ routes: [
{ {
path: '/', path: '/',
redirect: '/store/user/signin', redirect: '/open/user/signin',
}, },
{ {
path: '/store', path: '/open',
redirect: '/store/user/signin', redirect: '/open/user/signin',
}, },
{ {
path: '/store/home', path: '/open/home',
name: 'home', name: 'home',
component: () => import('views/Index.vue'), component: () => import('views/Index.vue'),
children: routes, children: [
{
path: '/open/home/plugin-shop',
name: 'plugin-shop',
component: () => import('views/index-list/plugin-shop.vue'),
},
{
path: '/open/home/console-desk',
name: 'console-desk',
component: () => import('views/index-list/console-desk.vue'),
children: [
{
path: '/open/home/console-desk/plugin-list',
name: 'desk-plugin-list',
component: () => import('views/index-list/plugin-list.vue'),
},
{
path: '/open/home/console-desk/add-plugin',
name: 'desk-add-plugin',
component: () => import('views/index-list/add-plugin.vue'),
},
{
path: '/open/home/console-desk/business-list',
name: 'desk-business-list',
component: () => import('views/index-list/business-list.vue'),
},
{
path: '/open/home/console-desk/add-business',
name: 'desk-add-business',
component: () => import('views/index-list/add-business.vue'),
},
],
},
{
path: '/open/home/business-detail',
name: 'business-detail',
component: () => import('views/index-list/business-detail.vue'),
},
],
}, },
...user, ...user,
], ],
}); });

17
src/store/index.js

@ -1,14 +1,27 @@
import { createStore } from 'vuex'; import { createStore } from 'vuex';
import user from './user'; import user from './user';
import plugin from './plugin';
export default createStore({ export default createStore({
modules: { user }, modules: { user, plugin },
state: { menu: { show: true, collapse: false } }, state: {
menu: { show: true, collapse: false },
systemInfo: null, // 系统设备信息
},
getters: {}, getters: {},
mutations: { mutations: {
toggleCollapse(state) { toggleCollapse(state) {
state.menu.collapse = !state.menu.collapse; state.menu.collapse = !state.menu.collapse;
}, },
/**
* 设置系统信息的数据
* @param {object} state
* @param {object | null} data 获取到的数据
*/
setSystemInfo(state, data) {
state.systemInfo = data;
},
}, },
actions: {}, actions: {},
}); });

26
src/store/plugin.js

@ -0,0 +1,26 @@
export default {
namespaced: true,
state: { leftIndex: 3, businessInfo: {} },
getters: {},
mutations: {
/**
* 设置控制台左侧菜单栏
* @param {*} state
* @param {number} data
*/
setLeftIndex(state, data) {
state.leftIndex = data;
},
/**
* 设置当前查看的业务信息
* @param {*} state
* @param {object|null} data
*/
setBusinessInfo(state, data) {
state.businessInfo = data;
},
},
actions: {},
};

3
src/utils/axios.js

@ -16,6 +16,9 @@ instance.interceptors.request.use(
if (token) { if (token) {
config.headers.Authorization = `Bearer ${token}`; config.headers.Authorization = `Bearer ${token}`;
} }
// config.headers.deviceId = store.state.systemInfo.deviceId;
config.headers.deviceId = '1';
config.headers.appType = 0;
return config; return config;
}, },
error => { error => {

18
src/utils/time.js

@ -0,0 +1,18 @@
const time = {
dateFormat(value) {
if (value) {
const date = new Date(Number(value)); // 时间戳为秒:13位数
// let date = new Date(value * 1000) // 时间戳为毫秒:10位数
const year = date.getFullYear();
const month = date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1;
const day = date.getDate() < 10 ? `0${date.getDate()}` : date.getDate();
const hour = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours();
const minute = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes();
const second = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds();
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
return '';
},
};
export default time;

33
src/views/Index.vue

@ -1,3 +1,34 @@
<template> <template>
index.vue <el-container>
<el-header>
<div class="top">
<Navbar />
</div>
</el-header>
<el-main>
<div class="bg-white p-6">
<router-view></router-view>
</div>
</el-main>
</el-container>
</template> </template>
<script>
export default {};
</script>
<style scoped>
.el-header {
background: #00182e;
color: #fff;
}
.el-main {
padding: 1.6rem 12rem;
background: #f5f5f5;
}
.top {
/* padding: 2rem 10rem; */
width: 100%;
}
</style>

140
src/views/index-list/add-business.vue

@ -0,0 +1,140 @@
<template>
<div class="box">
<h1 class="text-lg font-semibold">配置业务</h1>
<el-form ref="formRef" :model="form" :rules="rules" label-width="150px" class="forms">
<el-form-item prop="name">
<template v-slot:label>
<el-tooltip class="box-item" effect="dark" content="输入帮助" placement="top-end">
<div>业务名称</div>
</el-tooltip>
</template>
<el-input v-model="form.name" placeholder="输入业务名称"></el-input>
</el-form-item>
<el-form-item label="行业:" prop="region">
<el-select v-model="form.region" placeholder="请选择">
<el-option value="教育"></el-option>
<el-option value="医疗"></el-option>
</el-select>
</el-form-item>
<el-form-item label="分类:" prop="sort">
<el-select v-model="form.sort" multiple placeholder="请选择,可多选">
<el-option value="康复"></el-option>
<el-option value="管理"></el-option>
</el-select>
</el-form-item>
<el-form-item label="标签:" prop="tags">
<el-select v-model="form.tags" multiple filterable allow-create default-first-option placeholder="请选择标签">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="详情:" prop="description">
<el-input v-model="form.description" type="textarea" placeholder="请输入备注"></el-input>
</el-form-item>
<el-form-item label="是否公开:" prop="pub">
<el-switch v-model="form.pub" class="mr-3"></el-switch>
</el-form-item>
<el-form-item label="是否启用:" prop="startUsing">
<el-switch v-model="form.startUsing" class="mr-3"></el-switch>
</el-form-item>
<!-- <el-form-item label="是否开启debug模式:" prop="debug">
<el-switch v-model="form.debug" class="mr-3"></el-switch>
</el-form-item> -->
<el-form-item> <el-switch v-model="form.debug" class="mr-3"></el-switch> 是否开启debug模式 </el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit()">发布</el-button>
<el-button @click="resetForm()">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
import { saveBusiness } from '@/apis/business.js';
const formRef = ref(null);
const form = reactive({
name: '',
region: '',
sort: '',
tags: '',
description: '',
pub: false,
startUsing: false,
debug: false,
});
const rules = {
name: [{ required: true, message: '输入业务名称', trigger: 'blur' }],
region: [{ required: true, message: '请选择行业', trigger: 'blur' }],
sort: [{ required: true, message: '请选择分类', trigger: 'blur' }],
};
/**
* 创建业务
*/
const onSubmit = () => {
try {
formRef.value.validate(async valid => {
if (valid) {
const params = {};
params.param = form;
params.param.pub = form.pub ? 1 : 0;
params.param.startUsing = form.startUsing ? 1 : 0;
params.param.debug = form.debug ? 1 : 0;
await saveBusiness(params);
ElMessage.success('创建成功');
resetForm();
} else {
return false;
}
});
} catch (error) {
ElMessage.error(error || '创建失败');
console.log('error: ', error);
}
};
function resetForm() {
formRef.value.resetFields();
}
const options = [
{
value: 'HTML',
label: 'HTML',
},
{
value: 'CSS',
label: 'CSS',
},
{
value: 'JavaScript',
label: 'JavaScript',
},
];
</script>
<style scoped>
.el-form-item {
margin-top: 1rem;
}
.tags {
width: 100%;
}
.el-select {
width: 100%;
}
.box >>> .el-form-item__label {
display: flex;
flex-direction: row-reverse;
justify-content: end;
flex: 0 0 auto;
text-align: right;
font-size: var(--el-form-label-font-size);
color: var(--el-text-color-regular);
line-height: 40px;
padding: 0 12px 0 0;
box-sizing: border-box;
}
</style>

350
src/views/index-list/add-plugin.vue

@ -0,0 +1,350 @@
<template>
<div class="box">
<h1 class="text-lg font-semibold">上传插件</h1>
<el-form ref="formRef" :model="form" :rules="rules" label-width="150px" class="forms">
<el-form-item label="插件id:" prop="id">
<div v-if="form.id">
{{ form.id }}
<i class="el-icon-document-copy cursor-pointer ml-2" @click="copy(form.id)"></i>
</div>
<el-button type="success" plain size="mini" @click="handelPluginId" v-else>点击生成插件id</el-button>
</el-form-item>
<el-form-item prop="name">
<template v-slot:label>
<el-tooltip class="box-item" effect="dark" content="输入帮助" placement="top-end">
<div>插件名称 </div>
</el-tooltip>
</template>
<el-input v-model="form.name" placeholder="输入插件名称"></el-input>
</el-form-item>
<el-form-item label="版本:" prop="versions">
<el-input v-model="form.versions" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="简介:" prop="intro">
<el-input v-model="form.intro" type="textarea" placeholder="请输入备注"></el-input>
</el-form-item>
<el-form-item label="行业:" prop="region">
<el-select v-model="form.region" placeholder="请选择">
<el-option value="教育"></el-option>
<el-option value="医疗"></el-option>
</el-select>
</el-form-item>
<el-form-item label="分类:" prop="sort">
<el-select v-model="form.sort" multiple placeholder="请选择,可多选">
<el-option value="康复"></el-option>
<el-option value="管理"></el-option>
</el-select>
</el-form-item>
<el-form-item label="标签:" prop="tags">
<el-select v-model="form.tags" multiple filterable allow-create default-first-option placeholder="请选择标签">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="HTML:" prop="html">
<el-input v-model="form.html" type="textarea" placeholder="请输入HTML代码片段"></el-input>
</el-form-item>
<el-form-item label="JS:" prop="js">
<el-input v-model="form.js" type="textarea" placeholder="请输入JS代码片段"></el-input>
</el-form-item>
<el-form-item label="CSS:" prop="css">
<el-input v-model="form.css" type="textarea" placeholder="请输入CSS代码片段"></el-input>
</el-form-item>
<el-form-item label="配置文件:" prop="config">
<el-input v-model="form.config" type="textarea" placeholder="请输入配置文件"></el-input>
</el-form-item>
<el-form-item label="上传预览图:" prop="preview">
<el-upload
class="avatar-uploader"
:action="`${apiUrl}/filedeal/file/upload/single`"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="form.preview" :src="form.preview" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item prop="carousel">
<template v-slot:label>
<el-tooltip class="box-item" effect="dark" content="输入帮助" placement="top-end">
<div>上传轮播图 </div>
</el-tooltip>
</template>
<el-upload
class="upload-dialog"
name="files"
:action="`${apiUrl}/filedeal/file/upload/multiple`"
list-type="picture-card"
:on-success="handleBannerSuccess"
:on-remove="handleRemove"
ref="bannerUpload"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="form.dialogVisible">
<img width="100%" :src="form.banner" alt="" v-if="form.dialogVisible" />
</el-dialog>
</el-form-item>
<el-form-item label="README:" prop="description">
<el-input v-model="form.description" type="textarea" placeholder="请输入markdown详细介绍信息"></el-input>
</el-form-item>
<!-- <el-form-item> <el-switch v-model="form.delivery" class="mr-3"></el-switch> 是否压缩代码 </el-form-item> -->
<el-form-item>
<el-button type="primary" @click="onSubmit()">发布</el-button>
<el-button @click="resetForm()">重置</el-button>
<el-button type="success" @click="visible = !visible">{{ !visible ? '预览' : '关闭预览' }}</el-button>
<div class="plugin-box" v-if="visible">
<div class="plugin w-full h-full overflow-y-scroll">
<Plugin :plugin="form" />
</div>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import useClipboard from 'vue-clipboard3';
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
import { randomId, savePlugin } from '@/apis/plugin';
const { toClipboard } = useClipboard();
const apiUrl = import.meta.env.VITE_API_URL_NEW;
const formRef = ref(null);
const bannerUpload = ref(null);
const visible = ref(false);
const form = reactive({
id: '',
name: '',
versions: 'V1.0.0',
intro: '',
region: '',
sort: '',
tags: '',
html: '',
js: '',
css: '',
config: '',
delivery: '',
preview: '',
carousel: [],
dialogVisible: false,
description: '',
banner: '',
});
const rules = {
id: [{ required: true, message: '请先生成插件id', trigger: 'blur' }],
name: [{ required: true, message: '请输入插件名称', trigger: 'blur' }],
versions: [{ required: true, message: '请输入版本号', trigger: 'blur' }],
// region: [{ required: true, message: '', trigger: 'blur' }],
// sort: [{ required: true, message: '', trigger: 'blur' }],
// tags: [{ required: true, message: '', trigger: 'blur' }],
preview: [{ required: true, message: '请上传预览图', trigger: 'blur' }],
carousel: [{ required: true, message: '请上传轮播图', trigger: 'blur' }],
};
const options = [
{
value: 'HTML',
label: 'HTML',
},
{
value: 'CSS',
label: 'CSS',
},
{
value: 'JavaScript',
label: 'JavaScript',
},
];
/**
* 获取插件id
*/
async function handelPluginId() {
try {
const res = await randomId();
form.id = res;
} catch (error) {
console.error('error: ', error);
}
}
handelPluginId();
//
async function copy(Msg) {
try {
//
await toClipboard(Msg);
ElMessage.success('插件id复制成功');
} catch (e) {
//
console.error(e);
ElMessage.error('插件id复制失败');
}
}
/**
* 创建插件
* @param {object} params
*/
function onSubmit() {
try {
formRef.value.validate(async valid => {
if (valid) {
console.log('submit!');
const params = {};
params.param = form;
await savePlugin(params);
ElMessage.success('插件创建成功');
resetForm();
bannerUpload.value.clearFiles();
} else {
console.log('error submit!!');
return false;
}
});
} catch (error) {
ElMessage.error(error || '插件创建失败');
console.error('error: ', error);
}
}
/**
* 上传预览图
* @param {*} res
* @param {*} file
*/
function handleAvatarSuccess(res) {
if (res.data && res.data.visitUrl) {
form.preview = res.data.visitUrl;
}
}
function beforeAvatarUpload(file) {
// const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;
// if (!isJPG) {
// ElMessage.error(' JPG !');
// }
if (!isLt2M) {
ElMessage.error('上传头像图片大小不能超过 2MB!');
}
return isLt2M;
}
//
function handleRemove(res) {
if (res.response.data && res.response.data.length) {
const url = res.response.data[0].visitUrl;
const index = form.carousel.findIndex(item => item === url);
form.carousel.splice(index, 1);
}
}
//
function handleBannerSuccess(res) {
if (res.data && res.data.length) {
form.dialogVisible = true;
form.banner = res.data[0].visitUrl;
res.data.forEach(item => {
if (item && item.id) {
form.carousel.push(item.visitUrl);
}
});
}
}
//
function resetForm() {
formRef.value.resetFields();
handelPluginId();
}
</script>
<style scoped>
.box {
/* padding-left: 5%; */
}
.el-form-item {
margin-top: 1rem;
}
.tags {
width: 100%;
}
.el-select {
width: 100%;
}
.box >>> .el-form-item__label {
display: flex;
flex-direction: row-reverse;
justify-content: end;
flex: 0 0 auto;
text-align: right;
font-size: var(--el-form-label-font-size);
color: var(--el-text-color-regular);
line-height: 40px;
padding: 0 12px 0 0;
box-sizing: border-box;
}
.avatar-uploader >>> .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader >>> .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 104px;
height: 104px;
line-height: 104px;
text-align: center;
background: #fafafa;
}
.avatar {
width: 104px;
height: 104px;
display: block;
}
.upload-dialog >>> .el-upload--picture-card {
width: 104px;
height: 104px;
line-height: 104px;
background: #fafafa;
}
.upload-dialog >>> .el-upload-list--picture-card .el-upload-list__item {
width: 104px;
height: 104px;
}
.plugin-box {
position: fixed;
right: 14rem;
background: url(../../assets/iPhone13.png) no-repeat;
background-size: contain;
width: 19.375rem;
height: 39.2419rem;
top: calc(50vh - 289.93512px + 1.8rem);
padding: 2.8rem 1.2rem 2rem;
z-index: 99;
}
.plugin::-webkit-scrollbar {
display: none;
}
</style>

194
src/views/index-list/business-detail.vue

@ -0,0 +1,194 @@
<template>
<breadcrumb :path="data.path" />
<div class="pt-12">
<h1 class="text-lg font-semibold">业务名称</h1>
<div class="flex flex-col" v-if="data.info && data.info.id">
<div class="mt-3">
APPID: {{ data.info.appId }}
<i class="el-icon-document-copy cursor-pointer ml-2" @click="copy(data.info.appId)"></i>
</div>
<div class="mt-3">APPSecret: {{ data.info.secret }}</div>
<div class="mt-3">简介: {{ data.info.description }}</div>
<div class="flex flex-row mt-3">
<div class="text-sm mr-20" v-if="data.info.tags && data.info.tags.length">
<el-tag v-for="item in data.info.tags" :type="item.btnType" class="mr-3" :key="item.name">{{ item.name }}</el-tag>
</div>
<div>行业: {{ data.info.appId }}</div>
</div>
<div class="flex flex-row mt-3">
<div class="mr-20">创建时间: {{ time.dateFormat(data.info.createTime) }}</div>
<div>最新更新时间: {{ time.dateFormat(data.info.createTime) }}</div>
</div>
</div>
<div class="flex flex-col py-12">
<div class="flex flex-nowrap justify-between">
<h1 class="text-lg font-semibold">已绑定插件</h1>
<el-popover placement="bottom" title="请选择插件" :width="240" trigger="click">
<div class="radio-box">
<Relevance
:businessLists="data.pluginLists"
:businessId="data.info.id"
:isLastPage="data.isLastPage"
@changePageNum="changePageNum"
@query="queryPluginOfBusiness"
/>
</div>
<template #reference>
<el-button type="primary" @click="handleQueryPlugin">添加插件</el-button>
</template>
</el-popover>
</div>
<listPlugin :lists="data.lists" :showConfig="true" :businessId="data.businessId" @queryPluginOfBusiness="queryPluginOfBusiness" />
<Pagination
:currentPage="data.currentPage"
:pageSize="data.pageSize"
:total="data.total"
@handleSizeChange="handleSizeChange"
@handleCurrentChange="handleCurrentChange"
/>
</div>
</div>
</template>
<script setup>
import useClipboard from 'vue-clipboard3';
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import time from 'utils/time';
import { queryPluginByBusiness, queryIdBusiness } from '@/apis/business.js';
import { queryPlugins } from '@/apis/plugin.js';
const router = useRouter();
const { toClipboard } = useClipboard();
const data = reactive({
path: [
{ title: '控制台', name: 'plugin-shop' },
{ title: '我的业务', name: 'desk-business-list' },
{ title: '业务详情', name: 'desk-add-business' },
],
lists: [],
currentPage: 1,
pageSize: 10,
total: 0,
info: {},
pluginLists: [],
isLastPage: false,
pageNum: 1,
businessId: '',
});
function handleSizeChange(val) {
data.pageSize = val;
queryPluginOfBusiness();
}
function handleCurrentChange(val) {
data.currentPage = val;
queryPluginOfBusiness();
}
//
async function copy(Msg) {
try {
//
await toClipboard(Msg);
ElMessage.success('复制成功');
} catch (e) {
//
console.error(e);
ElMessage.error('复制失败');
}
}
/**
* 通过id查询业务信息
* @param {string} businessId 业务信息对应的id
*/
async function handleQueryIdBusiness(businessId) {
try {
const params = { param: { businessId } };
const res = await queryIdBusiness(params);
data.info = res;
} catch (error) {
console.error('error: ', error);
}
}
/**
* 查询业务下关联的插件
* @param {string} businessId 业务信息对应的id
* @param {number} name 插件名称为空则不实用该条件
* @param {number} pageNum 第几页
* @param {number} pageSize 每页几条信息
*/
async function queryPluginOfBusiness() {
try {
const { currentPage, pageSize, businessId } = data;
const params = {
param: {
businessId,
pageNum: currentPage,
pageSize,
},
};
const res = await queryPluginByBusiness(params);
data.lists = res.list;
data.currentPage = res.pageNum - 0;
data.pageSize = res.pageSize - 0;
data.total = res.total - 0;
} catch (error) {
console.error('error: ', error);
}
}
const routeValue = router.currentRoute.value;
const id = routeValue && routeValue.query && routeValue.query.id ? routeValue.query.id : '';
data.businessId = id;
queryPluginOfBusiness(id);
handleQueryIdBusiness(id);
function changePageNum() {
data.pageNum++;
handleQueryPlugin();
}
/**
* 查询插件列表
* @param {number} depth 查询深度 0则只查名称1则查询全部
* @param {number} name 插件名称为空则不实用该条件
* @param {number} pageNum 第几页
* @param {number} pageSize 每页几条信息
*/
async function handleQueryPlugin() {
try {
const { pageNum } = data;
if (pageNum === 1) {
data.pluginLists = [];
}
const params = {
param: {
depth: 0,
name: '',
pageNum,
pageSize: 10,
},
};
const res = await queryPlugins(params);
if (res.list.length) {
res.list.forEach(item => {
data.pluginLists.push(item);
});
}
data.pageNum = res.pageNum - 0;
data.isLastPage = res.isLastPage;
} catch (error) {
console.error('error: ', error);
}
}
</script>
<style></style>

79
src/views/index-list/business-list.vue

@ -0,0 +1,79 @@
<template>
<div class="flex flex-col">
<listSearchBar />
<div class="py-6">
<el-button type="primary" icon="el-icon-plus" @click="openPage">创建业务</el-button>
</div>
<listTable :lists="data.lists" @handleQueryBusiness="handleQueryBusiness" />
<Pagination
:currentPage="data.currentPage"
:pageSize="data.pageSize"
:total="data.total"
@handleSizeChange="handleSizeChange"
@handleCurrentChange="handleCurrentChange"
/>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { reactive } from 'vue';
import { queryBusiness } from '@/apis/business.js';
const router = useRouter();
const store = useStore();
const data = reactive({
lists: [],
currentPage: 1,
pageSize: 10,
total: 0,
});
function openPage() {
router.push({ name: 'desk-add-business' });
store.commit('plugin/setLeftIndex', 1);
}
function handleSizeChange(val) {
data.pageSize = val;
handleQueryBusiness();
}
function handleCurrentChange(val) {
data.currentPage = val;
handleQueryBusiness();
}
/**
* 查询插件列表
* @param {number} depth 查询深度 0则只查名称1则查询全部
* @param {number} name 插件名称为空则不实用该条件
* @param {number} pageNum 第几页
* @param {number} pageSize 每页几条信息
*/
async function handleQueryBusiness() {
try {
const { currentPage, pageSize } = data;
const params = {
param: {
depth: 1,
name: '',
pageNum: currentPage,
pageSize,
},
};
const res = await queryBusiness(params);
data.lists = res.list;
data.currentPage = res.pageNum - 0;
data.pageSize = res.pageSize - 0;
data.total = res.total - 0;
} catch (error) {
console.error('error: ', error);
}
}
handleQueryBusiness();
</script>
<style></style>

14
src/views/index-list/console-desk.vue

@ -0,0 +1,14 @@
<template>
<!-- <div class="flex flex-row"> -->
<el-container>
<el-aside width="208px">
<leftMenu />
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
<!-- </div> -->
</template>
<script setup></script>

82
src/views/index-list/plugin-list.vue

@ -0,0 +1,82 @@
<template>
<div class="flex flex-col">
<listSearchBar />
<div class="py-6">
<el-button type="primary" icon="el-icon-plus" @click="openPage">创建插件</el-button>
</div>
<PluginListTable :lists="data.lists" @handleQueryPlugins="handleQueryPlugins" />
<Pagination
:currentPage="data.currentPage"
:pageSize="data.pageSize"
:total="data.total"
@handleSizeChange="handleSizeChange"
@handleCurrentChange="handleCurrentChange"
/>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { reactive } from 'vue';
import { queryPlugins } from '@/apis/plugin.js';
const router = useRouter();
const store = useStore();
const data = reactive({
lists: [],
currentPage: 1,
pageSize: 10,
total: 0,
});
function openPage() {
router.push({ name: 'desk-add-plugin' });
store.commit('plugin/setLeftIndex', 2);
}
function handleSizeChange(val) {
data.pageSize = val;
handleQueryPlugins();
}
function handleCurrentChange(val) {
data.currentPage = val;
handleQueryPlugins();
}
/**
* 查询插件列表
* @param {number} depth 查询深度 0则只查名称1则查询全部
* @param {number} mine 查询全部插件还是自己的插件 0全部 1自己
* @param {number} name 插件名称为空则不实用该条件
* @param {number} pageNum 第几页
* @param {number} pageSize 每页几条信息
*/
async function handleQueryPlugins() {
try {
const { currentPage, pageSize } = data;
const params = {
param: {
depth: 1,
mine: 1,
name: '',
pageNum: currentPage,
pageSize,
},
};
const res = await queryPlugins(params);
data.lists = res.list;
data.currentPage = res.pageNum - 0;
data.pageSize = res.pageSize - 0;
data.total = res.total - 0;
} catch (error) {
console.error('error: ', error);
}
}
handleQueryPlugins();
</script>
<style></style>

67
src/views/index-list/plugin-shop.vue

@ -0,0 +1,67 @@
<template>
<searchBar />
<listPlugin :lists="data.lists" />
<Pagination
:currentPage="data.currentPage"
:pageSize="data.pageSize"
:total="data.total"
@handleSizeChange="handleSizeChange"
@handleCurrentChange="handleCurrentChange"
/>
</template>
<script lang="ts" setup="true">
import { reactive } from 'vue';
import { queryPlugins } from '@/apis/plugin.js';
const data = reactive({
lists: [],
currentPage: 1,
pageSize: 10,
total: 0,
});
function handleSizeChange(val) {
data.pageSize = val;
handleQueryPlugins();
}
function handleCurrentChange(val) {
data.currentPage = val;
handleQueryPlugins();
}
/**
* 查询插件列表
* @param {number} depth 查询深度 0则只查名称1则查询全部
* @param {number} mine 查询全部插件还是自己的插件 0全部 1自己
* @param {number} name 插件名称为空则不实用该条件
* @param {number} pageNum 第几页
* @param {number} pageSize 每页几条信息
*/
async function handleQueryPlugins() {
try {
const { currentPage, pageSize } = data;
const params = {
param: {
depth: 1,
mine: 0,
name: '',
pageNum: currentPage,
pageSize,
},
};
const res = await queryPlugins(params);
data.lists = res.list;
data.currentPage = res.pageNum - 0;
data.pageSize = res.pageSize - 0;
data.total = res.total - 0;
} catch (error) {
console.error('error: ', error);
}
}
handleQueryPlugins();
</script>
<style scoped></style>

2
src/views/user/pw-change.vue

@ -1,7 +1,7 @@
<template> <template>
<div class="wrap"> <div class="wrap">
<div class="content border rounded-md"> <div class="content border rounded-md">
<h1 class="text-2xl text-center font-bold mb-10 text-gray-700">智能大气腐蚀监测云平台</h1> <h1 class="text-2xl text-center font-bold mb-10 text-gray-700">时物链条插件商城</h1>
<el-form ref="passwordChangeFormRef" :model="passwordChangeForm" :rules="rules" label-width="70px" status-icon> <el-form ref="passwordChangeFormRef" :model="passwordChangeForm" :rules="rules" label-width="70px" status-icon>
<el-form-item class="mb-3" label="账号" prop="account" style="margin-bottom: 22px !important"> <el-form-item class="mb-3" label="账号" prop="account" style="margin-bottom: 22px !important">
<el-input v-model="passwordChangeForm.account" type="text"></el-input> <el-input v-model="passwordChangeForm.account" type="text"></el-input>

2
src/views/user/sign-in.vue

@ -71,7 +71,7 @@ function onSubmit() {
ElMessage.success('登录成功, 欢迎回来'); ElMessage.success('登录成功, 欢迎回来');
setTimeout(() => { setTimeout(() => {
// //
router.push({ name: 'home' }); router.push({ name: 'plugin-shop' });
}, 1000); }, 1000);
} catch (error) { } catch (error) {
ElMessage.error(error); ElMessage.error(error);

2
vite.config.js

@ -11,7 +11,7 @@ const resolve = dir => path.join(__dirname, dir);
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: '/store/', base: '/open/',
plugins: [vue(), VitePWA(), WindiCSS(), Components({ resolvers: [ElementPlusResolver()] }), viteCompression()], plugins: [vue(), VitePWA(), WindiCSS(), Components({ resolvers: [ElementPlusResolver()] }), viteCompression()],
resolve: { resolve: {
alias: { alias: {

166
yarn.lock

@ -37,7 +37,7 @@
"resolved" "https://registry.npmmirror.com/@babel/compat-data/download/@babel/compat-data-7.16.4.tgz?cache=0&sync_timestamp=1637102861835&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fcompat-data%2Fdownload%2F%40babel%2Fcompat-data-7.16.4.tgz" "resolved" "https://registry.npmmirror.com/@babel/compat-data/download/@babel/compat-data-7.16.4.tgz?cache=0&sync_timestamp=1637102861835&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fcompat-data%2Fdownload%2F%40babel%2Fcompat-data-7.16.4.tgz"
"version" "7.16.4" "version" "7.16.4"
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.1", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.4.0-0": "@babel/core@^7.11.1":
"integrity" "sha1-xP9EBG9f4xBSXMnrTvUUfwxTdNQ=" "integrity" "sha1-xP9EBG9f4xBSXMnrTvUUfwxTdNQ="
"resolved" "https://registry.npmmirror.com/@babel/core/download/@babel/core-7.16.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fcore%2Fdownload%2F%40babel%2Fcore-7.16.0.tgz" "resolved" "https://registry.npmmirror.com/@babel/core/download/@babel/core-7.16.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fcore%2Fdownload%2F%40babel%2Fcore-7.16.0.tgz"
"version" "7.16.0" "version" "7.16.0"
@ -1410,7 +1410,7 @@
"resolved" "https://registry.nlark.com/acorn-jsx/download/acorn-jsx-5.3.2.tgz?cache=0&sync_timestamp=1625793240297&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn-jsx%2Fdownload%2Facorn-jsx-5.3.2.tgz" "resolved" "https://registry.nlark.com/acorn-jsx/download/acorn-jsx-5.3.2.tgz?cache=0&sync_timestamp=1625793240297&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn-jsx%2Fdownload%2Facorn-jsx-5.3.2.tgz"
"version" "5.3.2" "version" "5.3.2"
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^7.1.1", "acorn@^7.4.0", "acorn@^8.5.0": "acorn@^7.1.1", "acorn@^7.4.0":
"integrity" "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=" "integrity" "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo="
"resolved" "https://registry.nlark.com/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1630916517167&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn%2Fdownload%2Facorn-7.4.1.tgz" "resolved" "https://registry.nlark.com/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1630916517167&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn%2Fdownload%2Facorn-7.4.1.tgz"
"version" "7.4.1" "version" "7.4.1"
@ -1423,7 +1423,7 @@
"clean-stack" "^2.0.0" "clean-stack" "^2.0.0"
"indent-string" "^4.0.0" "indent-string" "^4.0.0"
"ajv@^6.10.0", "ajv@^6.12.4", "ajv@>=8": "ajv@^6.10.0", "ajv@^6.12.4":
"integrity" "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=" "integrity" "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ="
"resolved" "https://registry.nlark.com/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1631470871211&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fajv%2Fdownload%2Fajv-6.12.6.tgz" "resolved" "https://registry.nlark.com/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1631470871211&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fajv%2Fdownload%2Fajv-6.12.6.tgz"
"version" "6.12.6" "version" "6.12.6"
@ -1784,6 +1784,15 @@
"resolved" "https://registry.npm.taobao.org/cli-width/download/cli-width-2.2.1.tgz" "resolved" "https://registry.npm.taobao.org/cli-width/download/cli-width-2.2.1.tgz"
"version" "2.2.1" "version" "2.2.1"
"clipboard@^2.0.6":
"integrity" "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ=="
"resolved" "https://registry.npmmirror.com/clipboard/download/clipboard-2.0.8.tgz"
"version" "2.0.8"
dependencies:
"good-listener" "^1.2.2"
"select" "^1.1.2"
"tiny-emitter" "^2.0.0"
"cliui@^7.0.2": "cliui@^7.0.2":
"integrity" "sha1-oCZe5lVHb8gHrqnfPfjfd4OAi08=" "integrity" "sha1-oCZe5lVHb8gHrqnfPfjfd4OAi08="
"resolved" "https://registry.npm.taobao.org/cliui/download/cliui-7.0.4.tgz?cache=0&sync_timestamp=1604880226973&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-7.0.4.tgz" "resolved" "https://registry.npm.taobao.org/cliui/download/cliui-7.0.4.tgz?cache=0&sync_timestamp=1604880226973&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-7.0.4.tgz"
@ -1915,7 +1924,7 @@
"browserslist" "^4.17.6" "browserslist" "^4.17.6"
"semver" "7.0.0" "semver" "7.0.0"
"cosmiconfig@^7.0.0", "cosmiconfig@^7.0.1", "cosmiconfig@>=6": "cosmiconfig@^7.0.0", "cosmiconfig@^7.0.1":
"integrity" "sha1-cU11ZSLKzoZ4Z8y0R0xdAbuuXW0=" "integrity" "sha1-cU11ZSLKzoZ4Z8y0R0xdAbuuXW0="
"resolved" "https://registry.nlark.com/cosmiconfig/download/cosmiconfig-7.0.1.tgz" "resolved" "https://registry.nlark.com/cosmiconfig/download/cosmiconfig-7.0.1.tgz"
"version" "7.0.1" "version" "7.0.1"
@ -1955,28 +1964,26 @@
"resolved" "https://registry.nlark.com/cz-conventional-changelog/download/cz-conventional-changelog-3.3.0.tgz" "resolved" "https://registry.nlark.com/cz-conventional-changelog/download/cz-conventional-changelog-3.3.0.tgz"
"version" "3.3.0" "version" "3.3.0"
dependencies: dependencies:
"@commitlint/load" ">6.1.1"
"chalk" "^2.4.1" "chalk" "^2.4.1"
"commitizen" "^4.0.3" "commitizen" "^4.0.3"
"conventional-commit-types" "^3.0.0" "conventional-commit-types" "^3.0.0"
"lodash.map" "^4.5.1" "lodash.map" "^4.5.1"
"longest" "^2.0.1" "longest" "^2.0.1"
"word-wrap" "^1.0.3" "word-wrap" "^1.0.3"
optionalDependencies:
"@commitlint/load" ">6.1.1"
"cz-conventional-changelog@3.2.0": "cz-conventional-changelog@3.2.0":
"integrity" "sha1-au8fiS1kETND1+RVUpCJrJ8g5Hc=" "integrity" "sha1-au8fiS1kETND1+RVUpCJrJ8g5Hc="
"resolved" "https://registry.nlark.com/cz-conventional-changelog/download/cz-conventional-changelog-3.2.0.tgz" "resolved" "https://registry.nlark.com/cz-conventional-changelog/download/cz-conventional-changelog-3.2.0.tgz"
"version" "3.2.0" "version" "3.2.0"
dependencies: dependencies:
"@commitlint/load" ">6.1.1"
"chalk" "^2.4.1" "chalk" "^2.4.1"
"commitizen" "^4.0.3" "commitizen" "^4.0.3"
"conventional-commit-types" "^3.0.0" "conventional-commit-types" "^3.0.0"
"lodash.map" "^4.5.1" "lodash.map" "^4.5.1"
"longest" "^2.0.1" "longest" "^2.0.1"
"word-wrap" "^1.0.3" "word-wrap" "^1.0.3"
optionalDependencies:
"@commitlint/load" ">6.1.1"
"dargs@^7.0.0": "dargs@^7.0.0":
"integrity" "sha1-BAFcQd4Ly2nshAUPPZvgyvjW1cw=" "integrity" "sha1-BAFcQd4Ly2nshAUPPZvgyvjW1cw="
@ -2044,6 +2051,11 @@
dependencies: dependencies:
"object-keys" "^1.0.12" "object-keys" "^1.0.12"
"delegate@^3.1.2":
"integrity" "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY="
"resolved" "https://registry.nlark.com/delegate/download/delegate-3.2.0.tgz"
"version" "3.2.0"
"detect-file@^1.0.0": "detect-file@^1.0.0":
"integrity" "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" "integrity" "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc="
"resolved" "https://registry.npm.taobao.org/detect-file/download/detect-file-1.0.0.tgz" "resolved" "https://registry.npm.taobao.org/detect-file/download/detect-file-1.0.0.tgz"
@ -2150,7 +2162,7 @@
"resolved" "https://registry.npmmirror.com/emoji-regex/download/emoji-regex-8.0.0.tgz?cache=0&sync_timestamp=1632751408145&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Femoji-regex%2Fdownload%2Femoji-regex-8.0.0.tgz" "resolved" "https://registry.npmmirror.com/emoji-regex/download/emoji-regex-8.0.0.tgz?cache=0&sync_timestamp=1632751408145&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Femoji-regex%2Fdownload%2Femoji-regex-8.0.0.tgz"
"version" "8.0.0" "version" "8.0.0"
"enquirer@^2.3.5", "enquirer@^2.3.6", "enquirer@>= 2.3.0 < 3": "enquirer@^2.3.5", "enquirer@^2.3.6":
"integrity" "sha1-Kn/l3WNKHkElqXXsmU/1RW3Dc00=" "integrity" "sha1-Kn/l3WNKHkElqXXsmU/1RW3Dc00="
"resolved" "https://registry.nlark.com/enquirer/download/enquirer-2.3.6.tgz" "resolved" "https://registry.nlark.com/enquirer/download/enquirer-2.3.6.tgz"
"version" "2.3.6" "version" "2.3.6"
@ -2209,11 +2221,91 @@
"is-date-object" "^1.0.1" "is-date-object" "^1.0.1"
"is-symbol" "^1.0.2" "is-symbol" "^1.0.2"
"esbuild-android-arm64@0.13.8":
"integrity" "sha1-wg6HXDyYFksf+6myhje9+W9ennw="
"resolved" "https://registry.npmmirror.com/esbuild-android-arm64/download/esbuild-android-arm64-0.13.8.tgz?cache=0&sync_timestamp=1634439415306&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-android-arm64%2Fdownload%2Fesbuild-android-arm64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-darwin-64@0.13.8":
"integrity" "sha1-9G5rRx3b9iJlI0gIpqGqkd8YpBc="
"resolved" "https://registry.npmmirror.com/esbuild-darwin-64/download/esbuild-darwin-64-0.13.8.tgz?cache=0&sync_timestamp=1634439397884&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-darwin-64%2Fdownload%2Fesbuild-darwin-64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-darwin-arm64@0.13.8":
"integrity" "sha1-qZEVemAT+s1PLhQVm32lJibJAVQ="
"resolved" "https://registry.npmmirror.com/esbuild-darwin-arm64/download/esbuild-darwin-arm64-0.13.8.tgz?cache=0&sync_timestamp=1634439409770&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-darwin-arm64%2Fdownload%2Fesbuild-darwin-arm64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-freebsd-64@0.13.8":
"integrity" "sha1-MBYB0uRDrUWJYONZtAKhfZUAvp0="
"resolved" "https://registry.npmmirror.com/esbuild-freebsd-64/download/esbuild-freebsd-64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-freebsd-arm64@0.13.8":
"integrity" "sha1-A5pjrMEuwIkgBsFH6iIeVfkSWp8="
"resolved" "https://registry.npmmirror.com/esbuild-freebsd-arm64/download/esbuild-freebsd-arm64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-linux-32@0.13.8":
"integrity" "sha1-xTe2fX5pS2C/onhlgUEoOMa6AoQ="
"resolved" "https://registry.npmmirror.com/esbuild-linux-32/download/esbuild-linux-32-0.13.8.tgz?cache=0&sync_timestamp=1634439427536&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-linux-32%2Fdownload%2Fesbuild-linux-32-0.13.8.tgz"
"version" "0.13.8"
"esbuild-linux-64@0.13.8": "esbuild-linux-64@0.13.8":
"integrity" "sha1-AJL8igZAAad3v6DjtCW7i+j5bmo=" "integrity" "sha1-AJL8igZAAad3v6DjtCW7i+j5bmo="
"resolved" "https://registry.npmmirror.com/esbuild-linux-64/download/esbuild-linux-64-0.13.8.tgz?cache=0&sync_timestamp=1634439421101&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-linux-64%2Fdownload%2Fesbuild-linux-64-0.13.8.tgz" "resolved" "https://registry.npmmirror.com/esbuild-linux-64/download/esbuild-linux-64-0.13.8.tgz?cache=0&sync_timestamp=1634439421101&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-linux-64%2Fdownload%2Fesbuild-linux-64-0.13.8.tgz"
"version" "0.13.8" "version" "0.13.8"
"esbuild-linux-arm@0.13.8":
"integrity" "sha1-rWNPlr8pdVNpB665/bdaMZT03c4="
"resolved" "https://registry.npmmirror.com/esbuild-linux-arm/download/esbuild-linux-arm-0.13.8.tgz"
"version" "0.13.8"
"esbuild-linux-arm64@0.13.8":
"integrity" "sha1-XNPyu5JCEpcUgujbwlxK/QmygRA="
"resolved" "https://registry.npmmirror.com/esbuild-linux-arm64/download/esbuild-linux-arm64-0.13.8.tgz?cache=0&sync_timestamp=1634439402514&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-linux-arm64%2Fdownload%2Fesbuild-linux-arm64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-linux-mips64le@0.13.8":
"integrity" "sha1-V4V+3+v5v2V2bci+FjfyF5yZBXI="
"resolved" "https://registry.npmmirror.com/esbuild-linux-mips64le/download/esbuild-linux-mips64le-0.13.8.tgz?cache=0&sync_timestamp=1634439403033&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-linux-mips64le%2Fdownload%2Fesbuild-linux-mips64le-0.13.8.tgz"
"version" "0.13.8"
"esbuild-linux-ppc64le@0.13.8":
"integrity" "sha1-/bgqBZpbhrsQ+0IJG0689Ii5zUY="
"resolved" "https://registry.npmmirror.com/esbuild-linux-ppc64le/download/esbuild-linux-ppc64le-0.13.8.tgz?cache=0&sync_timestamp=1634439402029&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-linux-ppc64le%2Fdownload%2Fesbuild-linux-ppc64le-0.13.8.tgz"
"version" "0.13.8"
"esbuild-netbsd-64@0.13.8":
"integrity" "sha1-14eecSPTssBHVOzovQYapoZt7v8="
"resolved" "https://registry.npmmirror.com/esbuild-netbsd-64/download/esbuild-netbsd-64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-openbsd-64@0.13.8":
"integrity" "sha1-iLKAtssKP2rbYKvyf8UGxQajXPA="
"resolved" "https://registry.npmmirror.com/esbuild-openbsd-64/download/esbuild-openbsd-64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-sunos-64@0.13.8":
"integrity" "sha1-Iprnx3AxlqWKzQ8Cka2b69qBXWM="
"resolved" "https://registry.npmmirror.com/esbuild-sunos-64/download/esbuild-sunos-64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-windows-32@0.13.8":
"integrity" "sha1-iS0JPjKiHAyRNeWg/9w4Cutw52M="
"resolved" "https://registry.npmmirror.com/esbuild-windows-32/download/esbuild-windows-32-0.13.8.tgz?cache=0&sync_timestamp=1634439444336&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-windows-32%2Fdownload%2Fesbuild-windows-32-0.13.8.tgz"
"version" "0.13.8"
"esbuild-windows-64@0.13.8":
"integrity" "sha1-fe/Y15rju35vU7ZacZC+fa+QFoY="
"resolved" "https://registry.npmmirror.com/esbuild-windows-64/download/esbuild-windows-64-0.13.8.tgz?cache=0&sync_timestamp=1634439403833&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild-windows-64%2Fdownload%2Fesbuild-windows-64-0.13.8.tgz"
"version" "0.13.8"
"esbuild-windows-arm64@0.13.8":
"integrity" "sha1-5ZrgBElv2KWrZ7/HlFouR0gNb7k="
"resolved" "https://registry.npmmirror.com/esbuild-windows-arm64/download/esbuild-windows-arm64-0.13.8.tgz"
"version" "0.13.8"
"esbuild@^0.13.2": "esbuild@^0.13.2":
"integrity" "sha1-vXzFG4gasGd4n4jhe6ynRyTB7E8=" "integrity" "sha1-vXzFG4gasGd4n4jhe6ynRyTB7E8="
"resolved" "https://registry.npmmirror.com/esbuild/download/esbuild-0.13.8.tgz?cache=0&sync_timestamp=1634439403556&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild%2Fdownload%2Fesbuild-0.13.8.tgz" "resolved" "https://registry.npmmirror.com/esbuild/download/esbuild-0.13.8.tgz?cache=0&sync_timestamp=1634439403556&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fesbuild%2Fdownload%2Fesbuild-0.13.8.tgz"
@ -2290,7 +2382,7 @@
dependencies: dependencies:
"htmlparser2" "^7.1.2" "htmlparser2" "^7.1.2"
"eslint-plugin-import@^2.22.1", "eslint-plugin-import@^2.25.2": "eslint-plugin-import@^2.25.2":
"integrity" "sha1-s7kWDv3bcC/BY2ZZ5xuh0Qrb6ek=" "integrity" "sha1-s7kWDv3bcC/BY2ZZ5xuh0Qrb6ek="
"resolved" "https://registry.npmmirror.com/eslint-plugin-import/download/eslint-plugin-import-2.25.2.tgz?cache=0&sync_timestamp=1634079318456&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-plugin-import%2Fdownload%2Feslint-plugin-import-2.25.2.tgz" "resolved" "https://registry.npmmirror.com/eslint-plugin-import/download/eslint-plugin-import-2.25.2.tgz?cache=0&sync_timestamp=1634079318456&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-plugin-import%2Fdownload%2Feslint-plugin-import-2.25.2.tgz"
"version" "2.25.2" "version" "2.25.2"
@ -2316,10 +2408,10 @@
dependencies: dependencies:
"prettier-linter-helpers" "^1.0.0" "prettier-linter-helpers" "^1.0.0"
"eslint-plugin-vue@^7.19.1": "eslint-plugin-vue@^7.20.0":
"integrity" "sha1-Q1+yznEoQqlTCyjqy4g2gOjqpPM=" "integrity" "sha512-oVNDqzBC9h3GO+NTgWeLMhhGigy6/bQaQbHS+0z7C4YEu/qK/yxHvca/2PTZtGNPsCrHwOTgKMrwu02A9iPBmw=="
"resolved" "https://registry.npmmirror.com/eslint-plugin-vue/download/eslint-plugin-vue-7.19.1.tgz" "resolved" "https://registry.npmmirror.com/eslint-plugin-vue/download/eslint-plugin-vue-7.20.0.tgz"
"version" "7.19.1" "version" "7.20.0"
dependencies: dependencies:
"eslint-utils" "^2.1.0" "eslint-utils" "^2.1.0"
"natural-compare" "^1.4.0" "natural-compare" "^1.4.0"
@ -2356,7 +2448,7 @@
"resolved" "https://registry.nlark.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz" "resolved" "https://registry.nlark.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz"
"version" "2.1.0" "version" "2.1.0"
"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^5.16.0 || ^6.8.0 || ^7.2.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0-0", "eslint@^7.32.0", "eslint@>=5.0.0", "eslint@>=7.0.0", "eslint@>=7.28.0": "eslint@^7.32.0":
"integrity" "sha1-xtMooUvj+wjI0dIeEsAv23oqgS0=" "integrity" "sha1-xtMooUvj+wjI0dIeEsAv23oqgS0="
"resolved" "https://registry.npmmirror.com/eslint/download/eslint-7.32.0.tgz?cache=0&sync_timestamp=1634180157931&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint%2Fdownload%2Feslint-7.32.0.tgz" "resolved" "https://registry.npmmirror.com/eslint/download/eslint-7.32.0.tgz?cache=0&sync_timestamp=1634180157931&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint%2Fdownload%2Feslint-7.32.0.tgz"
"version" "7.32.0" "version" "7.32.0"
@ -2703,6 +2795,11 @@
"resolved" "https://registry.nlark.com/fs.realpath/download/fs.realpath-1.0.0.tgz" "resolved" "https://registry.nlark.com/fs.realpath/download/fs.realpath-1.0.0.tgz"
"version" "1.0.0" "version" "1.0.0"
"fsevents@~2.3.2":
"integrity" "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro="
"resolved" "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536409579&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz"
"version" "2.3.2"
"function-bind@^1.1.1": "function-bind@^1.1.1":
"integrity" "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" "integrity" "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0="
"resolved" "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz" "resolved" "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz"
@ -2831,6 +2928,13 @@
dependencies: dependencies:
"type-fest" "^0.20.2" "type-fest" "^0.20.2"
"good-listener@^1.2.2":
"integrity" "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA="
"resolved" "https://registry.nlark.com/good-listener/download/good-listener-1.2.2.tgz"
"version" "1.2.2"
dependencies:
"delegate" "^3.1.2"
"graceful-fs@^4.1.6", "graceful-fs@^4.2.0": "graceful-fs@^4.1.6", "graceful-fs@^4.2.0":
"integrity" "sha1-5BK40z9eAGWTy9PO5t+fLOu+gCo=" "integrity" "sha1-5BK40z9eAGWTy9PO5t+fLOu+gCo="
"resolved" "https://registry.nlark.com/graceful-fs/download/graceful-fs-4.2.8.tgz" "resolved" "https://registry.nlark.com/graceful-fs/download/graceful-fs-4.2.8.tgz"
@ -3270,7 +3374,7 @@
"integrity" "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=" "integrity" "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="
"resolved" "https://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz?cache=0&sync_timestamp=1604161822397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsonfile%2Fdownload%2Fjsonfile-4.0.0.tgz" "resolved" "https://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz?cache=0&sync_timestamp=1604161822397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsonfile%2Fdownload%2Fjsonfile-4.0.0.tgz"
"version" "4.0.0" "version" "4.0.0"
optionalDependencies: dependencies:
"graceful-fs" "^4.1.6" "graceful-fs" "^4.1.6"
"jsonfile@^6.0.1": "jsonfile@^6.0.1":
@ -3278,9 +3382,8 @@
"resolved" "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz?cache=0&sync_timestamp=1604161822397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsonfile%2Fdownload%2Fjsonfile-6.1.0.tgz" "resolved" "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz?cache=0&sync_timestamp=1604161822397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsonfile%2Fdownload%2Fjsonfile-6.1.0.tgz"
"version" "6.1.0" "version" "6.1.0"
dependencies: dependencies:
"universalify" "^2.0.0"
optionalDependencies:
"graceful-fs" "^4.1.6" "graceful-fs" "^4.1.6"
"universalify" "^2.0.0"
"jsonparse@^1.2.0": "jsonparse@^1.2.0":
"integrity" "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" "integrity" "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
@ -3861,7 +3964,7 @@
dependencies: dependencies:
"fast-diff" "^1.1.2" "fast-diff" "^1.1.2"
"prettier@^2.4.1", "prettier@>=2.0.0": "prettier@^2.4.1":
"integrity" "sha1-Zx4RyJwUpM/Ids5WQQbEpnJsn1w=" "integrity" "sha1-Zx4RyJwUpM/Ids5WQQbEpnJsn1w="
"resolved" "https://registry.nlark.com/prettier/download/prettier-2.4.1.tgz?cache=0&sync_timestamp=1631777111780&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fprettier%2Fdownload%2Fprettier-2.4.1.tgz" "resolved" "https://registry.nlark.com/prettier/download/prettier-2.4.1.tgz?cache=0&sync_timestamp=1631777111780&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fprettier%2Fdownload%2Fprettier-2.4.1.tgz"
"version" "2.4.1" "version" "2.4.1"
@ -4098,7 +4201,7 @@
"serialize-javascript" "^4.0.0" "serialize-javascript" "^4.0.0"
"terser" "^5.0.0" "terser" "^5.0.0"
"rollup@^1.20.0 || ^2.0.0", "rollup@^1.20.0||^2.0.0", "rollup@^2.0.0", "rollup@^2.43.1", "rollup@^2.50.0", "rollup@^2.57.0": "rollup@^2.43.1", "rollup@^2.57.0":
"integrity" "sha1-pkOYM2Xnv39bfGKoMxuYO3xMZ/s=" "integrity" "sha1-pkOYM2Xnv39bfGKoMxuYO3xMZ/s="
"resolved" "https://registry.npmmirror.com/rollup/download/rollup-2.58.0.tgz?cache=0&sync_timestamp=1633071808909&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Frollup%2Fdownload%2Frollup-2.58.0.tgz" "resolved" "https://registry.npmmirror.com/rollup/download/rollup-2.58.0.tgz?cache=0&sync_timestamp=1633071808909&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Frollup%2Fdownload%2Frollup-2.58.0.tgz"
"version" "2.58.0" "version" "2.58.0"
@ -4146,6 +4249,11 @@
"resolved" "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz" "resolved" "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz"
"version" "2.1.2" "version" "2.1.2"
"select@^1.1.2":
"integrity" "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
"resolved" "https://registry.nlark.com/select/download/select-1.1.2.tgz"
"version" "1.1.2"
"semver-compare@^1.0.0": "semver-compare@^1.0.0":
"integrity" "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" "integrity" "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
"resolved" "https://registry.npm.taobao.org/semver-compare/download/semver-compare-1.0.0.tgz" "resolved" "https://registry.npm.taobao.org/semver-compare/download/semver-compare-1.0.0.tgz"
@ -4522,6 +4630,11 @@
dependencies: dependencies:
"readable-stream" "3" "readable-stream" "3"
"tiny-emitter@^2.0.0":
"integrity" "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM="
"resolved" "https://registry.nlark.com/tiny-emitter/download/tiny-emitter-2.1.0.tgz"
"version" "2.1.0"
"tmp@^0.0.33": "tmp@^0.0.33":
"integrity" "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=" "integrity" "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk="
"resolved" "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz" "resolved" "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz"
@ -4627,7 +4740,7 @@
"resolved" "https://registry.npmmirror.com/type-fest/download/type-fest-0.8.1.tgz" "resolved" "https://registry.npmmirror.com/type-fest/download/type-fest-0.8.1.tgz"
"version" "0.8.1" "version" "0.8.1"
"typescript@^4.4.3", "typescript@>=2.7", "typescript@4.4.3": "typescript@^4.4.3", "typescript@4.4.3":
"integrity" "sha1-vcVAfKorEJ79T4L+EwZW+XeikyQ=" "integrity" "sha1-vcVAfKorEJ79T4L+EwZW+XeikyQ="
"resolved" "https://registry.npmmirror.com/typescript/download/typescript-4.4.3.tgz" "resolved" "https://registry.npmmirror.com/typescript/download/typescript-4.4.3.tgz"
"version" "4.4.3" "version" "4.4.3"
@ -4777,7 +4890,7 @@
"debug" "^4.3.2" "debug" "^4.3.2"
"windicss" "^3.1.9" "windicss" "^3.1.9"
"vite@^2.0.0", "vite@^2.0.1", "vite@^2.3.0", "vite@^2.5.10", "vite@^2.6.4", "vite@>=2.0.0": "vite@^2.6.4":
"integrity" "sha1-PdfFAYElRICp6BUnij+EjNPk3zo=" "integrity" "sha1-PdfFAYElRICp6BUnij+EjNPk3zo="
"resolved" "https://registry.npmmirror.com/vite/download/vite-2.6.9.tgz?cache=0&sync_timestamp=1634546166900&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fvite%2Fdownload%2Fvite-2.6.9.tgz" "resolved" "https://registry.npmmirror.com/vite/download/vite-2.6.9.tgz?cache=0&sync_timestamp=1634546166900&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fvite%2Fdownload%2Fvite-2.6.9.tgz"
"version" "2.6.9" "version" "2.6.9"
@ -4789,6 +4902,13 @@
optionalDependencies: optionalDependencies:
"fsevents" "~2.3.2" "fsevents" "~2.3.2"
"vue-clipboard3@^1.0.1":
"integrity" "sha1-OeMfv0HxHTcBszcuOKf7g7SEeWo="
"resolved" "https://registry.nlark.com/vue-clipboard3/download/vue-clipboard3-1.0.1.tgz"
"version" "1.0.1"
dependencies:
"clipboard" "^2.0.6"
"vue-demi@*": "vue-demi@*":
"integrity" "sha1-YQGZL+RyTPVjQBihbpU/MFLpTio=" "integrity" "sha1-YQGZL+RyTPVjQBihbpU/MFLpTio="
"resolved" "https://registry.nlark.com/vue-demi/download/vue-demi-0.11.4.tgz" "resolved" "https://registry.nlark.com/vue-demi/download/vue-demi-0.11.4.tgz"
@ -4814,7 +4934,7 @@
dependencies: dependencies:
"@vue/devtools-api" "^6.0.0-beta.18" "@vue/devtools-api" "^6.0.0-beta.18"
"vue@^2.6.0 || ^3.2.0", "vue@^3.0.0", "vue@^3.0.0-0 || ^2.6.0", "vue@^3.0.2", "vue@^3.2.0", "vue@^3.2.16", "vue@2 || 3", "vue@3.2.20": "vue@^3.2.16":
"integrity" "sha1-lA+KqL8+O+eCQ8pYK61B/NRa4+Y=" "integrity" "sha1-lA+KqL8+O+eCQ8pYK61B/NRa4+Y="
"resolved" "https://registry.npmmirror.com/vue/download/vue-3.2.20.tgz?cache=0&sync_timestamp=1633712767060&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fvue%2Fdownload%2Fvue-3.2.20.tgz" "resolved" "https://registry.npmmirror.com/vue/download/vue-3.2.20.tgz?cache=0&sync_timestamp=1633712767060&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fvue%2Fdownload%2Fvue-3.2.20.tgz"
"version" "3.2.20" "version" "3.2.20"

Loading…
Cancel
Save