Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # components.d.ts # yarn.lock
This commit is contained in:
commit
cc50da7b49
|
@ -11,6 +11,8 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
yarn.lock
|
||||
components.d.ts
|
||||
|
||||
# Editor directories and files
|
||||
.vscode
|
||||
|
|
|
@ -1,22 +1,39 @@
|
|||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core';
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {};
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
ACard: typeof import('ant-design-vue/es')['Card'];
|
||||
ACol: typeof import('ant-design-vue/es')['Col'];
|
||||
ARow: typeof import('ant-design-vue/es')['Row'];
|
||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip'];
|
||||
BadgeStatus: typeof import('./src/components/BadgeStatus/index.vue')['default'];
|
||||
CardBox: typeof import('./src/components/CardBox/index.vue')['default'];
|
||||
GeoComponent: typeof import('./src/components/GeoComponent/index.vue')['default'];
|
||||
MonacoEditor: typeof import('./src/components/MonacoEditor/index.vue')['default'];
|
||||
RouterLink: typeof import('vue-router')['RouterLink'];
|
||||
RouterView: typeof import('vue-router')['RouterView'];
|
||||
ValueItem: typeof import('./src/components/ValueItem/index.vue')['default'];
|
||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
ACard: typeof import('ant-design-vue/es')['Card']
|
||||
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
||||
ACol: typeof import('ant-design-vue/es')['Col']
|
||||
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
||||
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||
AForm: typeof import('ant-design-vue/es')['Form']
|
||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
|
||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||
ARow: typeof import('ant-design-vue/es')['Row']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||
BadgeStatus: typeof import('./src/components/BadgeStatus/index.vue')['default']
|
||||
CardBox: typeof import('./src/components/CardBox/index.vue')['default']
|
||||
GeoComponent: typeof import('./src/components/GeoComponent/index.vue')['default']
|
||||
MonacoEditor: typeof import('./src/components/MonacoEditor/index.vue')['default']
|
||||
PermissionButton: typeof import('./src/components/PermissionButton/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
ValueItem: typeof import('./src/components/ValueItem/index.vue')['default']
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,9 +13,12 @@
|
|||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"@vuemap/vue-amap": "^1.1.20",
|
||||
"ant-design-vue": "^3.2.15",
|
||||
"axios": "^1.2.1",
|
||||
"js-cookie": "^3.0.1",
|
||||
"echarts": "^5.4.1",
|
||||
"less": "^4.1.3",
|
||||
"less-loader": "^11.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
@ -25,7 +28,8 @@
|
|||
"unplugin-auto-import": "^0.12.1",
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"vue": "^3.2.45",
|
||||
"vue-router": "^4.1.6"
|
||||
"vue-router": "^4.1.6",
|
||||
"jetlinks-store": "^0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.4.1",
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 159 B |
Binary file not shown.
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 551 KiB |
|
@ -0,0 +1,3 @@
|
|||
<svg width="18" height="15" viewBox="0 0 18 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 1.12527V13.5414C0 14.2185 0.476821 14.6667 1.20199 14.6667H16.798C17.5132 14.6667 18 14.2185 18 13.5414V1.12527C18 0.448201 17.5232 0 16.798 0H1.20199C0.486755 0 0 0.448201 0 1.12527ZM1.20199 1.12527H16.798V4.51062H1.20199V1.12527ZM16.798 13.5414H1.20199V10.156H16.798V13.5414ZM1.20199 9.03077V5.63589H16.798V9.02124L1.20199 9.03077ZM13.798 2.25054H15C15.3576 2.25054 15.596 2.47941 15.596 2.81318C15.596 3.15648 15.3576 3.37581 15 3.37581H13.798C13.4404 3.37581 13.202 3.14694 13.202 2.81318C13.202 2.47941 13.4404 2.25054 13.798 2.25054ZM15.596 7.33333C15.596 7.67664 15.3576 7.89597 15 7.89597H13.798C13.4404 7.89597 13.202 7.6671 13.202 7.33333C13.202 6.99957 13.4404 6.7707 13.798 6.7707H15C15.3576 6.7707 15.596 6.99003 15.596 7.33333ZM13.202 11.844C13.202 11.5006 13.4404 11.2813 13.798 11.2813H15C15.3576 11.2813 15.596 11.5102 15.596 11.844C15.596 12.1873 15.3576 12.4066 15 12.4066H13.798C13.4404 12.4161 13.202 12.1873 13.202 11.844Z" fill="#597EF7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="16" viewBox="0 0 12 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.49722 15.875C3.66823 15.875 4.61944 14.9238 4.61944 13.7528C4.61944 12.8205 4.01688 12.0285 3.17936 11.7424V4.39228C4.01688 4.10616 4.61944 3.31412 4.61944 2.38186C4.61944 1.21085 3.66823 0.259636 2.49722 0.259636C1.32621 0.259636 0.375 1.21085 0.375 2.38186C0.375 3.31412 0.977559 4.10805 1.81508 4.39228V11.7424C0.977559 12.0285 0.375 12.8205 0.375 13.7528C0.375 14.9238 1.32621 15.875 2.49722 15.875ZM1.86055 14.384C1.69051 14.2174 1.59256 13.9908 1.5877 13.7528C1.59256 13.5148 1.69051 13.2882 1.86055 13.1216C2.03059 12.9549 2.25916 12.8616 2.49722 12.8616C2.73527 12.8616 2.96385 12.9549 3.13388 13.1216C3.30392 13.2882 3.40188 13.5148 3.40674 13.7528C3.40188 13.9908 3.30392 14.2174 3.13388 14.384C2.96385 14.5506 2.73527 14.6439 2.49722 14.6439C2.25916 14.6439 2.03059 14.5506 1.86055 14.384ZM3.13388 1.75252C3.30392 1.91913 3.40188 2.14574 3.40674 2.38375C3.40188 2.62176 3.30392 2.84838 3.13388 3.01498C2.96385 3.18158 2.73527 3.2749 2.49722 3.2749C2.25916 3.2749 2.03059 3.18158 1.86055 3.01498C1.69051 2.84838 1.59256 2.62176 1.5877 2.38375C1.59256 2.14574 1.69051 1.91913 1.86055 1.75252C2.03059 1.58592 2.25916 1.49261 2.49722 1.49261C2.73527 1.49261 2.96385 1.58592 3.13388 1.75252ZM11.3016 12.8713H9.84711V11.2344H9.84952V4.25811C10.687 3.97199 11.2896 3.17994 11.2896 2.24768C11.2896 1.07667 10.3384 0.125463 9.16738 0.125463C7.99637 0.125463 7.04516 1.07667 7.04516 2.24768C7.04516 3.17994 7.64772 3.97388 8.48524 4.25811V10.4632L8.48351 10.4633L8.48524 10.8587V11.2344H8.48688L8.49405 12.8734H7.04163C6.91267 12.8734 6.8429 13.0214 6.92113 13.1208L9.05219 15.8163C9.0663 15.8346 9.0844 15.8494 9.1051 15.8595C9.12581 15.8697 9.14857 15.875 9.17164 15.875C9.19471 15.875 9.21747 15.8697 9.23818 15.8595C9.25889 15.8494 9.27699 15.8346 9.29109 15.8163L11.4222 13.1187C11.5004 13.0193 11.4285 12.8713 11.3016 12.8713ZM10.0769 2.2485C10.072 2.0105 9.97405 1.78388 9.80401 1.61727C9.63397 1.45067 9.4054 1.35736 9.16735 1.35736C8.92929 1.35736 8.70072 1.45067 8.53068 1.61727C8.36064 1.78388 8.26268 2.0105 8.25782 2.2485C8.26268 2.48651 8.36064 2.71313 8.53068 2.87973C8.70072 3.04634 8.92929 3.13965 9.16735 3.13965C9.4054 3.13965 9.63397 3.04634 9.80401 2.87973C9.97405 2.71313 10.072 2.48651 10.0769 2.2485Z" fill="#597EF7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,4 @@
|
|||
import { get } from '@/utils/request'
|
||||
|
||||
// 三方应用账户信息
|
||||
export const applicationInfo = (code: string) => get(`/application/sso/bind-code/${code}`)
|
|
@ -0,0 +1,15 @@
|
|||
import server from '@/utils/request'
|
||||
|
||||
export const config = () => server.get(`/authorize/captcha/config`)
|
||||
|
||||
export const code = () => server.get(`/authorize/captcha/image?width=130&height=30`)
|
||||
|
||||
export const authLogin = (data) => server.post(`/authorize/login`, data)
|
||||
|
||||
export const getInitSet = () => server.get(`/user/settings/init`)
|
||||
|
||||
export const postInitSet = (data) => server.post(`/user/settings/init`, data)
|
||||
|
||||
export const systemVersion = () => server.get(`/system/version`)
|
||||
|
||||
export const bindInfo = () => server.get(`/application/sso/_all`)
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<template v-if="isPermission">
|
||||
<template v-if="popConfirm">
|
||||
<a-popconfirm v-bind="popConfirm" :disabled="!isPermission || props.disabled">
|
||||
<a-tooltip v-if="tooltip" v-bind="tooltip">
|
||||
<slot v-if="noButton"></slot>
|
||||
<a-button v-else v-bind="buttonProps" :disabled="_isPermission"></a-button>
|
||||
</a-tooltip>
|
||||
<a-button v-else v-bind="buttonProps" :disabled="_isPermission"></a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
<template v-else-if="tooltip">
|
||||
<a-tooltip v-bind="tooltip">
|
||||
<slot v-if="noButton"></slot>
|
||||
<a-button v-else v-bind="buttonProps" :disabled="_isPermission"></a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot v-if="noButton"></slot>
|
||||
<a-button v-else v-bind="buttonProps" :disabled="_isPermission"></a-button>
|
||||
</template>
|
||||
</template>
|
||||
<a-tooltip v-else title="没有权限">
|
||||
<slot v-if="noButton"></slot>
|
||||
<a-button v-else v-bind="buttonProps" :disabled="_isPermission"></a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { ButtonProps, TooltipProps, PopconfirmProps } from 'ant-design-vue'
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
|
||||
interface PermissionButtonProps extends ButtonProps {
|
||||
tooltip?: TooltipProps;
|
||||
popConfirm?: PopconfirmProps;
|
||||
hasPermission?: string | Array<string>;
|
||||
noButton?: boolean;
|
||||
}
|
||||
const props = withDefaults(defineProps<PermissionButtonProps>(), {
|
||||
noButton: false
|
||||
})
|
||||
const { tooltip, popConfirm, hasPermission, noButton, ...buttonProps } = props;
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const isPermission = computed(() => {
|
||||
if (!props.hasPermission) {
|
||||
return true
|
||||
}
|
||||
return permissionStore.hasPermission(props.hasPermission)
|
||||
})
|
||||
const _isPermission = computed(() =>
|
||||
'hasPermission' in props && isPermission
|
||||
? 'disabled' in buttonProps
|
||||
? buttonProps.disabled
|
||||
: false
|
||||
: true
|
||||
)
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
|
@ -0,0 +1,45 @@
|
|||
.jtable-body {
|
||||
width: 100%;
|
||||
padding: 0 24px 24px;
|
||||
background-color: white;
|
||||
.jtable-body-header {
|
||||
padding: 16px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.jtable-body-header-right {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
.jtable-setting-item {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color-hover;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: @primary-color-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.jtable-content {
|
||||
.jtable-card {
|
||||
.jtable-card-items {
|
||||
display: grid;
|
||||
grid-gap: 26px;
|
||||
// grid-template-columns: repeat(4, 1fr);
|
||||
.jtable-card-item {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.jtable-pagination {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
bottom: 24px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue'
|
||||
import styles from './index.module.less'
|
||||
import { Space, Pagination, Table, Empty } from 'ant-design-vue'
|
||||
import type { TableProps } from 'ant-design-vue/es/table'
|
||||
enum ModelEnum {
|
||||
TABLE = 'TABLE',
|
||||
CARD = 'CARD',
|
||||
}
|
||||
|
||||
export declare type RequestData = {
|
||||
code: string;
|
||||
result: {
|
||||
data: any[] | undefined;
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
status: number;
|
||||
} & Record<string, any>;
|
||||
// interface ColumnType extends
|
||||
|
||||
interface JTableProps extends TableProps{
|
||||
// columns?: ColumnsType<RecordType>;
|
||||
request: (params: Record<string, any> & {
|
||||
pageSize?: number;
|
||||
pageIndex?: number;
|
||||
}) => Promise<Partial<RequestData>>;
|
||||
cardBodyClass?: string;
|
||||
}
|
||||
|
||||
|
||||
const JTable = defineComponent<JTableProps>({
|
||||
name: 'JTable',
|
||||
slots: [
|
||||
'headerTitle', // 顶部左边插槽
|
||||
'cardRender', // 卡片内容
|
||||
],
|
||||
emits: [
|
||||
'modelChange', // 切换卡片和表格
|
||||
],
|
||||
setup(props: JTableProps, { slots, emit }){
|
||||
const model = ref<keyof typeof ModelEnum>(ModelEnum.CARD); // 模式切换
|
||||
const column = ref<number>(3);
|
||||
console.log(props)
|
||||
const dataSource = ref<any[]>([
|
||||
{
|
||||
key: '1',
|
||||
name: '胡彦斌',
|
||||
age: 32,
|
||||
address: '西湖区湖底公园1号',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: '胡彦祖1',
|
||||
age: 42,
|
||||
address: '西湖区湖底公园1号',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: '胡彦斌',
|
||||
age: 32,
|
||||
address: '西湖区湖底公园1号',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: '胡彦祖1',
|
||||
age: 42,
|
||||
address: '西湖区湖底公园1号',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
name: '胡彦斌',
|
||||
age: 32,
|
||||
address: '西湖区湖底公园1号',
|
||||
},
|
||||
{
|
||||
key: '6',
|
||||
name: '胡彦祖1',
|
||||
age: 42,
|
||||
address: '西湖区湖底公园1号',
|
||||
},
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
|
||||
return () => <div class={styles["jtable-body"]}>
|
||||
<div class={styles["jtable-body-header"]}>
|
||||
<div class={styles["jtable-body-header-left"]}>
|
||||
{/* 顶部左边插槽 */}
|
||||
{slots.headerTitle && slots.headerTitle()}
|
||||
</div>
|
||||
<div class={styles["jtable-body-header-right"]}>
|
||||
{/* <Space> */}
|
||||
<div class={[styles["jtable-setting-item"], ModelEnum.CARD === model.value ? styles['active'] : '']} onClick={() => {
|
||||
model.value = ModelEnum.CARD
|
||||
}}>
|
||||
<AppstoreOutlined />
|
||||
</div>
|
||||
<div class={[styles["jtable-setting-item"], ModelEnum.TABLE === model.value ? styles['active'] : '']} onClick={() => {
|
||||
model.value = ModelEnum.TABLE
|
||||
}}>
|
||||
<UnorderedListOutlined />
|
||||
</div>
|
||||
{/* </Space> */}
|
||||
</div>
|
||||
</div>
|
||||
{/* content */}
|
||||
<div class={styles['jtable-content']}>
|
||||
{
|
||||
model.value === ModelEnum.CARD ?
|
||||
<div class={styles['jtable-card']}>
|
||||
{
|
||||
dataSource.value.length ?
|
||||
<div
|
||||
class={styles['jtable-card-items']}
|
||||
style={{gridTemplateColumns: `repeat(${column.value}, 1fr)`}}
|
||||
>
|
||||
{
|
||||
dataSource.value.map(item => slots.cardRender ?
|
||||
<div class={[styles['jtable-card-item'], props.cardBodyClass]}>{slots.cardRender(item)}</div>
|
||||
: null)
|
||||
}
|
||||
</div> :
|
||||
<div><Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /></div>
|
||||
}
|
||||
</div> :
|
||||
<div>
|
||||
<Table
|
||||
dataSource={dataSource.value}
|
||||
columns={props.columns}
|
||||
pagination={false}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
{/* 分页 */}
|
||||
{
|
||||
dataSource.value.length &&
|
||||
<div class={styles['jtable-pagination']}>
|
||||
<Pagination
|
||||
size="small"
|
||||
total={50}
|
||||
showTotal={(total) => {
|
||||
const min = 1
|
||||
const max = 1
|
||||
return `第 ${min} - ${max} 条/总共 ${total} 条`
|
||||
}}
|
||||
onChange={() => {
|
||||
|
||||
}}
|
||||
onShowSizeChange={() => {
|
||||
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
})
|
||||
|
||||
export default JTable
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div class="title">
|
||||
<div class="title-before"></div>
|
||||
<span>{{ data }}</span>
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TitleComponent",
|
||||
props: {
|
||||
data: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.title {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 10px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.title-before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: calc(100% - 2px);
|
||||
background-color: @primary-color;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
</style>
|
|
@ -1,8 +1,14 @@
|
|||
import type { App } from 'vue'
|
||||
import AIcon from './AIcon'
|
||||
import PermissionButton from './PermissionButton/index.vue'
|
||||
import JTable from './Table/index'
|
||||
import TitleComponent from "./TitleComponent/index.vue";
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
app.component('AIcon', AIcon)
|
||||
app.component('PermissionButton', PermissionButton)
|
||||
app.component('JTable', JTable)
|
||||
app.component('TitleComponent', TitleComponent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,43 @@ export default [
|
|||
// }
|
||||
|
||||
// start: 测试用, 可删除
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('@/views/user/Login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/demo',
|
||||
component: () => import('@/views/demo/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/bind',
|
||||
component: () => import('@/views/account/Center/bind/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/iot/home',
|
||||
component: () => import('@/views/iot/home/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/table',
|
||||
component: () => import('@/views/table/index.vue')
|
||||
},
|
||||
// end: 测试用, 可删除
|
||||
|
||||
// link 运维管理
|
||||
{
|
||||
path: '/link/log',
|
||||
component: () => import('@/views/link/Log/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/link/certificate',
|
||||
component: () => import('@/views/link/Certificate/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/link/certificate/detail/add',
|
||||
component: () => import('@/views/link/Certificate/Detail/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/link/accessConfig/detail/add',
|
||||
component: () => import('@/views/link/AccessConfig/Detail/index.vue')
|
||||
},
|
||||
]
|
|
@ -0,0 +1,240 @@
|
|||
/** 路由Code */
|
||||
export enum MENUS_CODE {
|
||||
'home' = 'home',
|
||||
'Analysis/CPU' = 'Analysis/CPU',
|
||||
'Analysis/DeviceChart' = 'Analysis/DeviceChart',
|
||||
'Analysis/DeviceMessage' = 'Analysis/DeviceMessage',
|
||||
'Analysis/Jvm' = 'Analysis/Jvm',
|
||||
'Analysis/MessageChart' = 'Analysis/MessageChart',
|
||||
'Analysis' = 'Analysis',
|
||||
'cloud/Aliyun' = 'cloud/Aliyun',
|
||||
'cloud/Ctwing' = 'cloud/Ctwing',
|
||||
'cloud/DuerOS' = 'cloud/DuerOS',
|
||||
'cloud/Onenet' = 'cloud/Onenet',
|
||||
'device/Alarm' = 'device/Alarm',
|
||||
'device/Category/Save' = 'device/Category/Save',
|
||||
'device/Category' = 'device/Category',
|
||||
'device/Command' = 'device/Command',
|
||||
'device/DataSource' = 'device/DataSource',
|
||||
'device/Instance' = 'device/Instance',
|
||||
'device/Location' = 'device/Location',
|
||||
'device/Product/Save' = 'device/Product/Save',
|
||||
'device/Product' = 'device/Product',
|
||||
'device/DashBoard' = 'device/DashBoard',
|
||||
'device/components/Alarm/Edit' = 'device/components/Alarm/Edit',
|
||||
'device/components/Alarm/Record' = 'device/components/Alarm/Record',
|
||||
'device/components/Alarm/Setting' = 'device/components/Alarm/Setting',
|
||||
'device/components/Alarm' = 'device/components/Alarm',
|
||||
'device/components/Metadata/Base/Edit' = 'device/components/Metadata/Base/Edit',
|
||||
'device/components/Metadata/Base' = 'device/components/Metadata/Base',
|
||||
'device/components/Metadata/Cat' = 'device/components/Metadata/Cat',
|
||||
'device/components/Metadata/Import' = 'device/components/Metadata/Import',
|
||||
'device/components/Metadata' = 'device/components/Metadata',
|
||||
'link/Certificate' = 'link/Certificate',
|
||||
'link/Certificate/Detail' = 'link/Certificate/Detail',
|
||||
'link/Gateway' = 'link/Gateway',
|
||||
'link/Protocol/Debug' = 'link/Protocol/Debug',
|
||||
'link/Protocol' = 'link/Protocol',
|
||||
'link/Type' = 'link/Type',
|
||||
'link/AccessConfig' = 'link/AccessConfig',
|
||||
'link/DataCollect/Dashboard' = 'link/DataCollect/Dashboard',
|
||||
'link/DataCollect/DataGathering' = 'link/DataCollect/DataGathering',
|
||||
'link/DataCollect/IntegratedQuery' = 'link/DataCollect/IntegratedQuery',
|
||||
'edge/Device' = 'edge/Device',
|
||||
'edge/Resource' = 'edge/Resource',
|
||||
'Log' = 'Log',
|
||||
'media/Cascade' = 'media/Cascade',
|
||||
'media/Cascade/Save' = 'media/Cascade/Save',
|
||||
'media/Cascade/Channel' = 'media/Cascade/Channel',
|
||||
'media/Config' = 'media/Config',
|
||||
'media/Device' = 'media/Device',
|
||||
'media/Device/Save' = 'media/Device/Save',
|
||||
'media/Device/Channel' = 'media/Device/Channel',
|
||||
'media/Device/Playback' = 'media/Device/Playback',
|
||||
'media/Reveal' = 'media/Reveal',
|
||||
'media/Stream' = 'media/Stream',
|
||||
'media/Stream/Detail' = 'media/Stream/Detail',
|
||||
'media/DashBoard' = 'media/DashBoard',
|
||||
'notice/Type' = 'notice/Type',
|
||||
'notice/Config' = 'notice/Config',
|
||||
'media/SplitScreen' = 'media/SplitScreen',
|
||||
'notice/Type/Config' = 'notice/Config',
|
||||
'notice/Config/Detail' = 'notice/Config/Detail',
|
||||
'notice/Template' = 'notice/Template',
|
||||
'notice/Template/Detail' = 'notice/Template/Detail',
|
||||
'rule-engine/DashBoard' = 'rule-engine/DashBoard',
|
||||
'rule-engine/Instance' = 'rule-engine/Instance',
|
||||
'rule-engine/SQLRule' = 'rule-engine/SQLRule',
|
||||
'rule-engine/Scene' = 'rule-engine/Scene',
|
||||
'rule-engine/Alarm/Log' = 'rule-engine/Alarm/Log',
|
||||
'rule-engine/Alarm/Log/Detail' = 'rule-engine/Alarm/Log/Detail',
|
||||
'rule-engine/Alarm/Config' = 'rule-engine/Alarm/Config',
|
||||
'rule-engine/Scene/Save' = 'rule-engine/Scene/Save',
|
||||
'rule-engine/Scene/Save2' = 'rule-engine/Scene/Save2',
|
||||
'rule-engine/Alarm/Configuration' = 'rule-engine/Alarm/Configuration',
|
||||
'rule-engine/Alarm/Configuration/Save' = 'rule-engine/Alarm/Configuration/Save',
|
||||
'simulator/Device' = 'simulator/Device',
|
||||
'system/DataSource' = 'system/DataSource',
|
||||
'system/DataSource/Management' = 'system/DataSource/Management',
|
||||
'system/Department/Assets' = 'system/Department/Assets',
|
||||
'system/Department/Member' = 'system/Department/Member',
|
||||
'system/Department' = 'system/Department',
|
||||
'system/Menu' = 'system/Menu',
|
||||
'system/Menu/Setting' = 'system/Menu/Setting',
|
||||
'system/OpenAPI' = 'system/OpenAPI',
|
||||
'system/Permission' = 'system/Permission',
|
||||
'system/Role/Detail' = 'system/Role/Detail',
|
||||
'system/Role' = 'system/Role',
|
||||
'system/Tenant/Detail/Assets' = 'system/Tenant/Detail/Assets',
|
||||
'system/Tenant/Detail/Info' = 'system/Tenant/Detail/Info',
|
||||
'system/Tenant/Detail/Member' = 'system/Tenant/Detail/Member',
|
||||
'system/Tenant/Detail/Permission' = 'system/Tenant/Detail/Permission',
|
||||
'system/Tenant/Detail' = 'system/Tenant/Detail',
|
||||
'system/Tenant' = 'system/Tenant',
|
||||
'system/User' = 'system/User',
|
||||
'system/Relationship' = 'system/Relationship',
|
||||
'system/Basis' = 'system/Basis',
|
||||
'user/Login' = 'user/Login',
|
||||
'visualization/Category' = 'visualization/Category',
|
||||
'visualization/Configuration' = 'visualization/Configuration',
|
||||
'visualization/Screen' = 'visualization/Screen',
|
||||
'device/Firmware' = 'device/Firmware',
|
||||
'device/Firmware/Task' = 'device/Firmware/Task',
|
||||
'device/Firmware/Task/Detail' = 'device/Firmware/Task/Detail',
|
||||
'device/Instance/Detail/Config/Tags' = 'device/Instance/Detail/Config/Tags',
|
||||
'device/Instance/Detail/Config' = 'device/Instance/Detail/Config',
|
||||
'device/Instance/Detail/Functions' = 'device/Instance/Detail/Functions',
|
||||
'device/Instance/Detail/Info' = 'device/Instance/Detail/Info',
|
||||
'device/Instance/Detail/Log' = 'device/Instance/Detail/Log',
|
||||
'device/Instance/Detail/MetadataLog/Event' = 'device/Instance/Detail/MetadataLog/Event',
|
||||
'device/Instance/Detail/MetadataLog/Property' = 'device/Instance/Detail/MetadataLog/Property',
|
||||
'device/Instance/Detail/Running' = 'device/Instance/Detail/Running',
|
||||
'device/Instance/Detail' = 'device/Instance/Detail',
|
||||
'device/Product/Detail/BaseInfo' = 'device/Product/Detail/BaseInfo',
|
||||
'device/Product/Detail' = 'device/Product/Detail',
|
||||
'link/AccessConfig/Detail' = 'link/AccessConfig/Detail',
|
||||
'link/DashBoard' = 'link/DashBoard',
|
||||
'system/Menu/Detail' = 'system/Menu/Detail',
|
||||
'system/Department/Detail' = 'system/Department/Detail',
|
||||
'link/Type/Detail' = 'link/Type/Detail',
|
||||
'account/Center' = 'account/Center',
|
||||
'account/NotificationSubscription' = 'account/NotificationSubscription',
|
||||
'account/NotificationRecord' = 'account/NotificationRecord',
|
||||
'account/Center/bind' = 'account/Center/bind',
|
||||
'Northbound/DuerOS' = 'Northbound/DuerOS',
|
||||
'Northbound/DuerOS/Detail' = 'Northbound/DuerOS/Detail',
|
||||
'Northbound/AliCloud' = 'Northbound/AliCloud',
|
||||
'Northbound/AliCloud/Detail' = 'Northbound/AliCloud/Detail',
|
||||
'system/Platforms' = 'system/Platforms',
|
||||
'system/Platforms/Api' = 'system/Platforms/Api',
|
||||
'system/Platforms/View' = 'system/Platforms/View',
|
||||
'system/Platforms/Setting' = 'system/Platforms/Setting',
|
||||
'system/Apply' = 'system/Apply',
|
||||
'system/Apply/Api' = 'system/Apply/Api',
|
||||
'system/Apply/View' = 'system/Apply/View',
|
||||
'system/License' = 'system/License',
|
||||
'iot-card/Home' = 'iot-card/Home',
|
||||
'iot-card/Platform' = 'iot-card/Platform',
|
||||
'iot-card/Platform/Detail' = 'iot-card/Platform/Detail',
|
||||
'iot-card/Recharge' = 'iot-card/Recharge',
|
||||
'iot-card/Dashboard' = 'iot-card/Dashboard',
|
||||
'iot-card/CardManagement' = 'iot-card/CardManagement',
|
||||
'iot-card/Record' = 'iot-card/Record',
|
||||
}
|
||||
|
||||
export type MENUS_CODE_TYPE = keyof typeof MENUS_CODE | string;
|
||||
|
||||
export enum BUTTON_PERMISSION_ENUM {
|
||||
'add' = 'add',
|
||||
'delete' = 'delete',
|
||||
'import' = 'import',
|
||||
'view' = 'view',
|
||||
'export' = 'export',
|
||||
'update' = 'update',
|
||||
'action' = 'action',
|
||||
'push' = 'push',
|
||||
'assert' = 'assert',
|
||||
'bind-user' = 'bind-user',
|
||||
'active' = 'active',
|
||||
'sync' = 'sync',
|
||||
'channel' = 'channel',
|
||||
'debug' = 'debug',
|
||||
'log' = 'log',
|
||||
'tigger' = 'tigger',
|
||||
'empowerment' = 'empowerment',
|
||||
'bind' = 'bind',
|
||||
'edit' = 'edit', //资产权限编辑
|
||||
'setting' = 'setting', //菜单配置
|
||||
'password' = 'password', //重置密码
|
||||
'api' = 'api', //查看api
|
||||
'manage' = 'manage', //数据源-管理
|
||||
'stop' = 'stop',
|
||||
'restart' = 'restart',
|
||||
'pay' = 'pay', //充值
|
||||
}
|
||||
|
||||
// 调试按钮、通知记录、批量导出、批量导入、选择通道、推送、分配资产、绑定用户对应的ID是啥
|
||||
export type CUSTOM_BUTTON = 'debug' | 'log' | 'channel' | 'assert' | 'bind-user';
|
||||
|
||||
export type BUTTON_PERMISSION = keyof typeof BUTTON_PERMISSION_ENUM | string | CUSTOM_BUTTON;
|
||||
|
||||
export const getDetailNameByCode = {
|
||||
'system/Menu/Detail': '菜单详情',
|
||||
'device/Product/Detail': '产品详情',
|
||||
'device/Instance/Detail': '设备详情',
|
||||
'device/Firmware/Task/Detail': '详情',
|
||||
'system/Department/Detail': '组织详情',
|
||||
'system/Role/Detail': '权限配置',
|
||||
'link/Type/Detail': '网络组件详情',
|
||||
'link/AccessConfig/Detail': '配置详情',
|
||||
'media/Stream/Detail': '流媒体详情',
|
||||
'rule-engine/Alarm/Log/Detail': '告警日志',
|
||||
'Northbound/AliCloud/Detail': '阿里云详情',
|
||||
'link/Certificate/Detail': '证书详情',
|
||||
'iot-card/Platform/Detail': '平台对接详情',
|
||||
};
|
||||
|
||||
// 开源版路由
|
||||
export const CommunityCodeList = [
|
||||
'account/Center',
|
||||
'account/NotificationSubscription',
|
||||
'account/NotificationRecord',
|
||||
'system/Basis',
|
||||
'system/User',
|
||||
'system/Department',
|
||||
'system/Department/Detail',
|
||||
'system/Role',
|
||||
'system/Role/Detail',
|
||||
'system/Menu',
|
||||
'system/Menu/Detail',
|
||||
'system/Menu/Setting',
|
||||
'system/Permission',
|
||||
'system/Relationship',
|
||||
'home',
|
||||
'rule-engine/DashBoard',
|
||||
'rule-engine/Alarm/Configuration',
|
||||
'rule-engine/Alarm/Configuration/Save',
|
||||
'rule-engine/Alarm/Log',
|
||||
'rule-engine/Alarm/Log/Detail',
|
||||
'device/DashBoard',
|
||||
'device/Category',
|
||||
'device/Instance',
|
||||
'device/Instance/Detail',
|
||||
'device/Product',
|
||||
'device/Product/Detail',
|
||||
'link/AccessConfig',
|
||||
'link/AccessConfig/Detail',
|
||||
'link/Protocol',
|
||||
'link/DashBoard',
|
||||
'Log',
|
||||
'link/Type',
|
||||
'link/Type/Detail',
|
||||
'link/Certificate',
|
||||
'link/Certificate/Detail',
|
||||
'rule-engine/Scene',
|
||||
'rule-engine/Scene/Save',
|
||||
'notice/Config',
|
||||
'notice/Config/Detail',
|
||||
'notice/Template',
|
||||
'notice/Template/Detail',
|
||||
];
|
|
@ -0,0 +1,42 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type { MENUS_CODE_TYPE, BUTTON_PERMISSION } from '@/router/router'
|
||||
|
||||
export const usePermissionStore = defineStore({
|
||||
id: 'permission',
|
||||
state: () => ({
|
||||
permissions: {} as {[key: MENUS_CODE_TYPE]: BUTTON_PERMISSION},
|
||||
}),
|
||||
getters: {
|
||||
check(state) {
|
||||
return (permissionCode: string) => {
|
||||
if (!permissionCode) {
|
||||
return true
|
||||
}
|
||||
if (!permissionCode.includes(":")) {
|
||||
return false
|
||||
}
|
||||
const code = permissionCode.split(":")[0]
|
||||
const value = permissionCode.split(":")[1]
|
||||
const _buttonArray = state.permissions[code]
|
||||
if (!_buttonArray) {
|
||||
return false
|
||||
}
|
||||
return _buttonArray.includes(value)
|
||||
}
|
||||
},
|
||||
hasPermission(state) {
|
||||
return (permissionCode: string | string[]) => {
|
||||
if (!permissionCode) {
|
||||
return true
|
||||
}
|
||||
if (!!Object.keys(state.permissions).length) {
|
||||
if (typeof permissionCode === 'string') {
|
||||
return this.check(permissionCode)
|
||||
}
|
||||
return permissionCode.some(_permissionCode => this.check(_permissionCode))
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,42 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { authLogin } from '@/api/login';
|
||||
import { LocalStore } from '@/utils/comm';
|
||||
import { TOKEN_KEY } from '@/utils/variable';
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
* @methods setUserInfos 设置用户信息
|
||||
*/
|
||||
export const useUserInfo = defineStore('userInfo', {
|
||||
state: () => ({
|
||||
userInfos: {
|
||||
id: '',
|
||||
username: '',
|
||||
isAdmin: true,
|
||||
currentAuthority: [],
|
||||
expires: 0,
|
||||
permissions: [],
|
||||
roles: [],
|
||||
token: '',
|
||||
user: {},
|
||||
},
|
||||
}),
|
||||
actions: {
|
||||
login(userInfo: any) {
|
||||
const username = userInfo.userName.trim();
|
||||
const password = userInfo.password;
|
||||
const verifyCode = userInfo.verifyCode;
|
||||
return new Promise((resolve: any, reject: any) => {
|
||||
authLogin({ username, password, verifyCode })
|
||||
.then((res: any) => {
|
||||
Object.assign(this.userInfos, res.result);
|
||||
LocalStore.set(TOKEN_KEY, res?.result.token);
|
||||
resolve(res);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -28,3 +28,39 @@ export const StatusColorEnum = {
|
|||
'warning': 'warning',
|
||||
'default': 'default',
|
||||
}
|
||||
|
||||
class SystemConst {
|
||||
static API_BASE = 'api';
|
||||
|
||||
static SYSTEM_NAME = 'Jetlinks';
|
||||
|
||||
static LOGIN = 'LOGIN-STATUS';
|
||||
|
||||
static DOC_URL = 'http://doc.jetlinks.cn';
|
||||
|
||||
static BASE_CURD_MODAL_VISIBLE = 'BASE_CURD_MODAL_VISIBLE';
|
||||
|
||||
static BASE_CURD_CURRENT = 'BASE_CURD_CURRENT';
|
||||
|
||||
static BASE_CURD_MODEL = 'BASE_CURD_MODEL';
|
||||
|
||||
static BASE_UPDATE_DATA = 'BASE_UPDATE_DATA';
|
||||
|
||||
static GLOBAL_WEBSOCKET = 'GLOBAL-WEBSOCKET';
|
||||
|
||||
static BIND_USER_STATE = 'false';
|
||||
|
||||
static REFRESH_METADATA = 'refresh_metadata';
|
||||
|
||||
static REFRESH_METADATA_TABLE = 'refresh_metadata_table';
|
||||
|
||||
static GET_METADATA = 'get_metadata';
|
||||
|
||||
static REFRESH_DEVICE = 'refresh_device';
|
||||
|
||||
static AMAP_KEY = 'amap_key';
|
||||
|
||||
static Version_Code = 'version_code';
|
||||
}
|
||||
|
||||
export default SystemConst;
|
||||
|
|
|
@ -66,10 +66,10 @@ export const patch = function(url: string, data = {}) {
|
|||
* GET method request
|
||||
* @param {String} url
|
||||
* @param {Object} [params]
|
||||
* @param {Object} [ext], 扩展参数
|
||||
* @param {Object} [ext] 扩展参数
|
||||
* @returns {AxiosInstance}
|
||||
*/
|
||||
export const get = function(url: string, params = {}, ext: any) {
|
||||
export const get = function(url: string, params = {}, ext?: any) {
|
||||
return request({
|
||||
method: 'GET',
|
||||
url,
|
||||
|
@ -82,10 +82,10 @@ export const get = function(url: string, params = {}, ext: any) {
|
|||
* DELETE method request
|
||||
* @param {String} url
|
||||
* @param {Object} [params]
|
||||
* @param {Object} [ext], 扩展参数
|
||||
* @param {Object} [ext] 扩展参数
|
||||
* @returns {AxiosInstance}
|
||||
*/
|
||||
export const remove = function(url: string, params = {}, ext: any) {
|
||||
export const remove = function(url: string, params = {}, ext?: any) {
|
||||
return request({
|
||||
method: 'DELETE',
|
||||
url,
|
||||
|
@ -162,7 +162,7 @@ request.interceptors.request.use(config => {
|
|||
// 让每个请求携带自定义 token 请根据实际情况自行修改
|
||||
const token = LocalStore.get(TOKEN_KEY)
|
||||
// const token = store.$state.tokenAlias
|
||||
if (token) {
|
||||
if (!token) {
|
||||
setTimeout(() => {
|
||||
router.replace({
|
||||
path: LoginPath
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export const BASE_API_PATH = import.meta.env.VITE_APP_BASE_API
|
||||
|
||||
export const TOKEN_KEY = 'X-Access-Token'
|
||||
|
||||
export const Version_Code = 'version_code'
|
|
@ -0,0 +1,271 @@
|
|||
<!-- 第三方账户绑定 -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="content">
|
||||
<div class="title">第三方账户绑定</div>
|
||||
<!-- 已登录-绑定三方账号 -->
|
||||
<template v-if="false">
|
||||
<div class="info">
|
||||
<a-card style="width: 280px">
|
||||
<template #title>
|
||||
<div class="info-head">
|
||||
<img :src="getImage('/bind/Rectangle.png')" />
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="info-body">
|
||||
<img :src="getImage('/bind/jetlinksLogo.png')" />
|
||||
<p>账号:admin</p>
|
||||
<p>用户名:超级管理员</p>
|
||||
</div>
|
||||
</a-card>
|
||||
<img :src="getImage('/bind/Vector.png')" />
|
||||
<a-card style="width: 280px">
|
||||
<template #title>
|
||||
<div class="info-head">
|
||||
<img :src="getImage('/bind/Rectangle.png')" />
|
||||
<span>三方账户信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="info-body">
|
||||
<img :src="getImage('/bind/wechat-webapp.png')" />
|
||||
<p>用户名:-</p>
|
||||
<p>名称:微信昵称</p>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
<div class="btn">
|
||||
<a-button type="primary">立即绑定</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 未登录-绑定三方账号 -->
|
||||
<template v-else>
|
||||
<div class="not-login">
|
||||
<div class="logo">
|
||||
<img :src="getImage('/bind/jetlinksLogo.png')" />
|
||||
<img
|
||||
class="arrow"
|
||||
:src="getImage('/bind/Vector.png')"
|
||||
/>
|
||||
<img :src="getImage('/bind/wechat-webapp.png')" />
|
||||
</div>
|
||||
<div class="desc">
|
||||
你已通过微信授权,完善以下登录信息即可以完成绑定
|
||||
</div>
|
||||
<div class="login-form">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item
|
||||
label="账户"
|
||||
v-bind="validateInfos.username"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.username"
|
||||
placeholder="请输入账户"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码"
|
||||
v-bind="validateInfos.password"
|
||||
>
|
||||
<a-input-password
|
||||
v-model:value="formData.password"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="验证码"
|
||||
v-bind="validateInfos.captcha"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.captcha"
|
||||
placeholder="请输入验证码"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<span style="cursor: pointer">
|
||||
图形验证码
|
||||
</span>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
style="width: 100%"
|
||||
>
|
||||
登录并绑定账户
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { Form } from 'ant-design-vue';
|
||||
|
||||
import { applicationInfo } from '@/api/bind';
|
||||
|
||||
const useForm = Form.useForm;
|
||||
|
||||
interface formData {
|
||||
username: string;
|
||||
password: string;
|
||||
captcha: string;
|
||||
}
|
||||
|
||||
// 三方应用信息
|
||||
const getAppInfo = async () => {
|
||||
const code: string = '73ab60c88979a1475963a5dde31e374b';
|
||||
const res = await applicationInfo(code);
|
||||
console.log('getAppInfo: ', res);
|
||||
};
|
||||
getAppInfo();
|
||||
|
||||
// 登录表单
|
||||
const formData = ref<formData>({
|
||||
username: '',
|
||||
password: '',
|
||||
captcha: '',
|
||||
});
|
||||
const formRules = ref({
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入账户',
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密码',
|
||||
},
|
||||
],
|
||||
captcha: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入验证码',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { resetFields, validate, validateInfos } = useForm(
|
||||
formData.value,
|
||||
formRules.value,
|
||||
);
|
||||
|
||||
/**
|
||||
* 登录并绑定账户
|
||||
*/
|
||||
const handleSubmit = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
console.log('toRaw:', toRaw(formData.value));
|
||||
console.log('formData.value:', formData.value);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('error', err);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(
|
||||
.ant-form-item-label
|
||||
> label.ant-form-item-required:not(
|
||||
.ant-form-item-required-mark-optional
|
||||
)::before
|
||||
) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.ant-form-item-label > label) {
|
||||
font-weight: bold;
|
||||
}
|
||||
.page-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: url(/images/bind/bindPage.png) 0% 0% / 100% 100% no-repeat;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
width: 850px;
|
||||
min-height: 510px;
|
||||
background: #fff;
|
||||
border: 1px solid #e0e4e8;
|
||||
border-radius: 2px;
|
||||
.title {
|
||||
margin: 30px 0;
|
||||
color: #0f1222;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
font-family: 'PingFang SC';
|
||||
font-style: normal;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
// 已登录-绑定三方账号
|
||||
.info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
&-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
}
|
||||
&-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
img {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
// 未登录
|
||||
.not-login {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
.arrow {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
.desc {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
font-family: 'PingFang SC';
|
||||
font-style: normal;
|
||||
line-height: 14px;
|
||||
opacity: 0.75;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,7 +2,7 @@
|
|||
<template>
|
||||
<div class="page-container">
|
||||
父级: {{ testValue }}
|
||||
<ViewItem v-model="testValue" />
|
||||
<ValueItem v-model="testValue" />
|
||||
<!-- 卡片 -->
|
||||
<br />卡片组件:
|
||||
<a-row :gutter="20">
|
||||
|
@ -41,7 +41,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ViewItem from '@/components/ValueItem/index.vue';
|
||||
import CardBox from '@/components/CardBox/index.vue';
|
||||
import { StatusColorEnum } from '@/utils/consts';
|
||||
import { getImage } from '@/utils/comm';
|
||||
|
|
|
@ -13,20 +13,34 @@
|
|||
<div class="box-item">
|
||||
<div class="label">CPU使用率</div>
|
||||
<div class="value">{{ cpu }}</div>
|
||||
<div class="chart" ref="cpuChart"></div>
|
||||
<Pie
|
||||
class="chart"
|
||||
chart-ref="cpuChart"
|
||||
:value="20"
|
||||
:color-arr="['#ebebeb', '#d3adf7']"
|
||||
image="/images/home/top-3.svg"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-item">
|
||||
<div class="label">JVM内存</div>
|
||||
<div class="value">{{ jvm }}</div>
|
||||
<div class="chart" ref="jvmChart"></div>
|
||||
<Pie
|
||||
class="chart"
|
||||
chart-ref="jvmChart"
|
||||
:value="31"
|
||||
:color-arr="['#d6e4ff', '#85a5ff']"
|
||||
image="/images/home/top-4.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const cpu = ref('0%');
|
||||
const jvm = ref('10%');
|
||||
import Pie from './Pie.vue';
|
||||
|
||||
const cpu = ref('20%');
|
||||
const jvm = ref('31%');
|
||||
|
||||
const jumpPage = () => {};
|
||||
</script>
|
||||
|
|
|
@ -11,7 +11,11 @@
|
|||
>
|
||||
<div class="item-english">{{ item.english }}</div>
|
||||
<div class="item-title">{{ item.label }}</div>
|
||||
<img class="item-image" :src="`/images/home/${index + 1}.png`" alt="" />
|
||||
<img
|
||||
class="item-image"
|
||||
:src="`/images/home/${index + 1}.png`"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
|
@ -25,7 +29,7 @@ type configItem = {
|
|||
link: string;
|
||||
english: string;
|
||||
label: string;
|
||||
save?: boolean;
|
||||
params?: object;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -35,13 +39,25 @@ const props = defineProps({
|
|||
});
|
||||
const { cardData, cardTitle } = toRefs(props);
|
||||
|
||||
const jumpPage = (linkItem: configItem): void => {
|
||||
if (linkItem.auth && linkItem.link) {
|
||||
router.push(`${linkItem.link}${linkItem.save ? '?save=true' : ''}`);
|
||||
const jumpPage = (row: configItem): void => {
|
||||
if (row.auth && row.link) {
|
||||
router.push(`${row.link}${objToParams(row.params || {})}`);
|
||||
} else {
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
}
|
||||
};
|
||||
|
||||
const objToParams = (source: object): string => {
|
||||
if (Object.prototype.toString.call(source) === '[object Object]') {
|
||||
const paramsArr = <any>[];
|
||||
Object.entries(source).forEach(([prop, value]) => {
|
||||
if (typeof value === 'object') value = JSON.stringify(value);
|
||||
paramsArr.push(`${prop}=${value}`);
|
||||
});
|
||||
if (paramsArr.length > 0) return '?' + paramsArr.join('&');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
<template>
|
||||
<a-card class="boot-card-container" :bordered="false">
|
||||
<template #title>
|
||||
<h5 class="title">{{ cardTitle }}</h5>
|
||||
</template>
|
||||
<div class="box">
|
||||
<div
|
||||
class="box-item"
|
||||
v-for="(item, index) in cardData"
|
||||
@click="jumpPage(item)"
|
||||
>
|
||||
<div class="item-content">
|
||||
<div class="icon">
|
||||
<img :src="item.image" alt="" />
|
||||
</div>
|
||||
<div class="intro">
|
||||
<div class="item-english">{{ item.english }}</div>
|
||||
<div class="item-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-bj">
|
||||
<img :src="`/images/home/home-${index + 1}.png`" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
type configItem = {
|
||||
auth: boolean;
|
||||
link: string;
|
||||
english: string;
|
||||
label: string;
|
||||
params?: object;
|
||||
image: string;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
cardData: Array<configItem>,
|
||||
cardTitle: String,
|
||||
});
|
||||
const { cardData, cardTitle } = toRefs(props);
|
||||
|
||||
const jumpPage = (row: configItem): void => {
|
||||
if (row.auth && row.link) {
|
||||
router.push(`${row.link}${objToParams(row.params || {})}`);
|
||||
} else {
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
}
|
||||
};
|
||||
|
||||
const objToParams = (source: object): string => {
|
||||
if (Object.prototype.toString.call(source) === '[object Object]') {
|
||||
const paramsArr = <any>[];
|
||||
// 直接使用for in遍历对象ts会报错
|
||||
Object.entries(source).forEach(([prop, value]) => {
|
||||
if (typeof value === 'object') value = JSON.stringify(value);
|
||||
paramsArr.push(`${prop}=${value}`);
|
||||
});
|
||||
if (paramsArr.length > 0) return '?' + paramsArr.join('&');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.boot-card-container {
|
||||
:deep(.ant-card-body) {
|
||||
padding-top: 0;
|
||||
}
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
padding-left: 18px;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #1d39c4;
|
||||
border: 1px solid #b4c0da;
|
||||
transform: translateY(-50%);
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
.box {
|
||||
font-size: 14px;
|
||||
.box-item {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-width: 1px 1px 1px 2px;
|
||||
border-style: solid;
|
||||
border-color: rgb(238, 238, 238) rgb(238, 238, 238)
|
||||
rgb(238, 238, 238) rgb(133, 165, 255);
|
||||
padding: 11px;
|
||||
background: linear-gradient(
|
||||
135.62deg,
|
||||
#f6f7fd 22.27%,
|
||||
rgba(255, 255, 255, 0.86) 91.82%
|
||||
);
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 4px 18px #efefef;
|
||||
&:not(:first-child) {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.item-label {
|
||||
color: #252526;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.right-bj {
|
||||
position: absolute;
|
||||
right: 10%;
|
||||
bottom: 0;
|
||||
width: 37px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,190 @@
|
|||
<template>
|
||||
<div class="comprehensive-home-conatiner">
|
||||
<a-row :gutter="24" class="top" style="margin-bottom: 24px">
|
||||
<a-col :span="6" class="left">
|
||||
<BootCardSmall
|
||||
:cardData="devBootConfig"
|
||||
cardTitle="物联网引导"
|
||||
/>
|
||||
<div style="width: 100%; height: 24px"></div>
|
||||
<BootCardSmall :cardData="opsBootConfig" cardTitle="运维引导" />
|
||||
</a-col>
|
||||
<a-col :span="18" class="right">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12"><DeviceCountCard /></a-col>
|
||||
<a-col :span="12"><BasicCountCard /></a-col>
|
||||
<a-col :span="24" style="margin-top: 24px">
|
||||
<PlatformPicCard image="/images/home/content1.png" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<StepCard
|
||||
cardTitle="设备接入推荐步骤"
|
||||
tooltip="不同的设备因为通信协议的不同,存在接入步骤的差异"
|
||||
:dataList="devStepDetails"
|
||||
style="margin-bottom: 24px"
|
||||
/>
|
||||
<StepCard
|
||||
cardTitle="运维管理推荐步骤"
|
||||
tooltip="请根据业务需要对下述步骤进行选择性操作。"
|
||||
:dataList="opsStepDetails"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ComprehensiveHome">
|
||||
import BootCardSmall from '../BootCardSmall.vue';
|
||||
import DeviceCountCard from '../DeviceCountCard.vue';
|
||||
import BasicCountCard from '../BasicCountCard.vue';
|
||||
import PlatformPicCard from '../PlatformPicCard.vue';
|
||||
import { recommendList } from '../../index';
|
||||
|
||||
import StepCard from '../StepCard.vue';
|
||||
|
||||
// 物联网引导-数据
|
||||
const devBootConfig = [
|
||||
{
|
||||
english: 'STEP1',
|
||||
label: '创建产品',
|
||||
link: '/a',
|
||||
auth: true,
|
||||
save: true,
|
||||
image: '/images/home/guide-home1.png',
|
||||
},
|
||||
{
|
||||
english: 'STEP2',
|
||||
label: '创建设备',
|
||||
link: '/b',
|
||||
auth: true,
|
||||
save: true,
|
||||
image: '/images/home/guide-home2.png',
|
||||
},
|
||||
{
|
||||
english: 'STEP3',
|
||||
label: '规则引擎',
|
||||
link: '/c',
|
||||
auth: false,
|
||||
save: true,
|
||||
image: '/images/home/guide-home3.png',
|
||||
},
|
||||
];
|
||||
// 运维管理引导-数据
|
||||
const opsBootConfig = [
|
||||
{
|
||||
english: 'STEP1',
|
||||
label: '创建产品',
|
||||
link: '/a',
|
||||
auth: true,
|
||||
save: true,
|
||||
image: '/images/home/guide-home4.png',
|
||||
},
|
||||
{
|
||||
english: 'STEP2',
|
||||
label: '创建设备',
|
||||
link: '/b',
|
||||
auth: true,
|
||||
save: true,
|
||||
image: '/images/home/guide-home5.png',
|
||||
},
|
||||
{
|
||||
english: 'STEP3',
|
||||
label: '规则引擎',
|
||||
link: '/c',
|
||||
auth: false,
|
||||
save: true,
|
||||
image: '/images/home/guide-home6.png',
|
||||
},
|
||||
];
|
||||
// 设备接入推荐步骤-数据
|
||||
const devStepDetails = [
|
||||
{
|
||||
title: '创建产品',
|
||||
details:
|
||||
'产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
|
||||
iconUrl: '/images/home/bottom-4.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
},
|
||||
{
|
||||
title: '配置产品接入方式',
|
||||
details:
|
||||
'通过产品对同一类型的设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
|
||||
iconUrl: '/images/home/bottom-1.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
dialogTag: 'accessMethod',
|
||||
},
|
||||
{
|
||||
title: '添加测试设备',
|
||||
details: '添加单个设备,用于验证产品模型是否配置正确。',
|
||||
iconUrl: '/images/home/bottom-5.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
},
|
||||
{
|
||||
title: '功能调试',
|
||||
details:
|
||||
'对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
|
||||
iconUrl: '/images/home/bottom-2.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
dialogTag: 'funcTest',
|
||||
},
|
||||
{
|
||||
title: '批量添加设备',
|
||||
details: '批量添加同一产品下的设备',
|
||||
iconUrl: '/images/home/bottom-3.png',
|
||||
linkUrl: '/a',
|
||||
auth: false,
|
||||
},
|
||||
] as recommendList[];
|
||||
// 运维管理推荐步骤-数据
|
||||
const opsStepDetails = [
|
||||
{
|
||||
title: '协议管理',
|
||||
details:
|
||||
'根据业务需求自定义开发对应的产品(设备模型)接入协议,并上传到平台。',
|
||||
iconUrl: '/images/home/bottom-1.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
params: {
|
||||
a: 1,
|
||||
save: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '证书管理',
|
||||
details: '统一维护平台内的证书,用于数据通信加密。',
|
||||
iconUrl: '/images/home/bottom-6.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
params: {
|
||||
a: 1,
|
||||
save: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '网络组件',
|
||||
details: '根据不同的传输类型配置平台底层网络组件相关参数。',
|
||||
iconUrl: '/images/home/bottom-3.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
},
|
||||
{
|
||||
title: '设备接入网关',
|
||||
details: '根据不同的传输类型,关联消息协议,配置设备接入网关相关参数。',
|
||||
iconUrl: '/images/home/bottom-4.png',
|
||||
linkUrl: '/a',
|
||||
auth: true,
|
||||
},
|
||||
{
|
||||
title: '日志管理',
|
||||
details: '监控系统日志,及时处理系统异常。',
|
||||
iconUrl: '/images/home/bottom-5.png',
|
||||
linkUrl: '/a',
|
||||
auth: false,
|
||||
},
|
||||
];
|
||||
</script>
|
|
@ -26,7 +26,6 @@ import BootCard from '../BootCard.vue';
|
|||
import DeviceCountCard from '../DeviceCountCard.vue';
|
||||
import PlatformPicCard from '../PlatformPicCard.vue';
|
||||
import StepCard from '../StepCard.vue';
|
||||
|
||||
// import {getImage} from '@/utils/comm'
|
||||
|
||||
// 物联网引导-数据
|
||||
|
@ -36,21 +35,21 @@ const bootConfig = [
|
|||
label: '创建产品',
|
||||
link: '/a',
|
||||
auth: true,
|
||||
save: true,
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
english: 'STEP2',
|
||||
label: '创建设备',
|
||||
link: '/b',
|
||||
auth: true,
|
||||
save: true,
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
english: 'STEP3',
|
||||
label: '规则引擎',
|
||||
link: '/c',
|
||||
auth: false,
|
||||
save: true,
|
||||
params: {},
|
||||
},
|
||||
];
|
||||
// 设备接入推荐步骤-数据
|
||||
|
|
|
@ -2,12 +2,43 @@
|
|||
<div class="init-home-container">
|
||||
<div class="title">请选择首页视图</div>
|
||||
|
||||
|
||||
|
||||
<div class="choose-view">
|
||||
<a-row class="view-content" :gutter="24">
|
||||
<a-col
|
||||
:span="8"
|
||||
class="select-item"
|
||||
:class="{ selected: selectId === '1' }"
|
||||
@click="selectId = '1'"
|
||||
>
|
||||
<img src="/images/home/device.png" alt="" />
|
||||
</a-col>
|
||||
<a-col
|
||||
:span="8"
|
||||
class="select-item"
|
||||
:class="{ selected: selectId === '2' }"
|
||||
@click="selectId = '2'"
|
||||
>
|
||||
<img src="/images/home/ops.png" alt="" />
|
||||
</a-col>
|
||||
<a-col
|
||||
:span="8"
|
||||
class="select-item"
|
||||
:class="{ selected: selectId === '3' }"
|
||||
@click="selectId = '3'"
|
||||
>
|
||||
<img src="/images/home/comprehensive.png" alt="" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button type="primary" class="btn" @click="confirm">确定</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
const selectId = ref('1');
|
||||
|
||||
const confirm = ()=>{}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.init-home-container {
|
||||
|
@ -26,5 +57,29 @@
|
|||
font-size: 26px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.choose-view {
|
||||
width: 100%;
|
||||
.view-content {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
.select-item {
|
||||
border: 2px solid transparent;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: #10239e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
margin: 48px auto;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<div :ref="props.chartRef"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
chartRef: String,
|
||||
value: Number,
|
||||
image: String,
|
||||
colorArr: Array<string>,
|
||||
});
|
||||
|
||||
const options = computed(() => ({
|
||||
color: props.colorArr || ['#D3ADF7', '#979AFF'],
|
||||
graphic: [
|
||||
{
|
||||
type: 'image',
|
||||
style: {
|
||||
image: props.image || '',
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
left: 'center',
|
||||
top: '41%',
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['100%', '60%'],
|
||||
center: ['50%', '50%'],
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
data: [100 - (props.value || 0), props.value],
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
watch(options, () => {
|
||||
initChart();
|
||||
});
|
||||
const initChart = () => {
|
||||
nextTick(() => {
|
||||
const myChart = echarts.init(proxy.$refs[props.chartRef]);
|
||||
|
||||
myChart.clear();
|
||||
myChart.setOption(options.value);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
});
|
||||
};
|
||||
initChart();
|
||||
</script>
|
|
@ -4,11 +4,19 @@
|
|||
<span>平台架构图</span>
|
||||
<p>PLATFORM ARCHITECTURE DIAGRAM</p>
|
||||
</div>
|
||||
<img src="/images/home/content.png" class="bj" alt="" />
|
||||
<img
|
||||
:src="props.image || '/images/home/content.png'"
|
||||
class="bj"
|
||||
alt=""
|
||||
/>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
image: String,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-card-body) {
|
||||
|
@ -18,7 +26,7 @@
|
|||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
min-height: 480px;
|
||||
border-bottom: 1px solid #2f54eb;
|
||||
|
||||
.bj {
|
||||
display: block;
|
||||
|
@ -41,7 +49,7 @@
|
|||
position: absolute;
|
||||
top: 50px;
|
||||
width: 100%;
|
||||
opacity: .3;
|
||||
opacity: 0.3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,37 +23,54 @@
|
|||
<div class="box-details">{{ item.details }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialogs">
|
||||
<AccessMethodDialog :open-number="openAccess" />
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
interface listConfig {
|
||||
title: string;
|
||||
details: string;
|
||||
iconUrl: string;
|
||||
linkUrl: string;
|
||||
params?: object;
|
||||
auth: boolean;
|
||||
}
|
||||
import AccessMethodDialog from './dialogs/AccessMethodDialog.vue';
|
||||
|
||||
import { recommendList } from '../index';
|
||||
|
||||
const props = defineProps({
|
||||
cardTitle: String,
|
||||
tooltip: String,
|
||||
dataList: Array<listConfig>,
|
||||
dataList: Array as PropType<recommendList[]>,
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
const { cardTitle, tooltip, dataList } = toRefs(props);
|
||||
const openAccess = ref<number>(0);
|
||||
const openFunc = ref<number>(0);
|
||||
|
||||
const jumpPage = (row: listConfig) => {
|
||||
if (row.auth && row.linkUrl) {
|
||||
router.push(`${row.linkUrl}${row.params ? '?save=true' : ''}`);
|
||||
} else {
|
||||
message.warning('暂无权限,请联系管理员');
|
||||
const jumpPage = (row: recommendList) => {
|
||||
if (!row.auth) return message.warning('暂无权限,请联系管理员');
|
||||
else if (row.dialogTag == 'accessMethod') return (openAccess.value += 1);
|
||||
else if (row.dialogTag === 'funcTest') return (openFunc.value += 1);
|
||||
else if (row.linkUrl) {
|
||||
router.push(`${row.linkUrl}${objToParams(row.params || {})}`);
|
||||
}
|
||||
};
|
||||
|
||||
const objToParams = (source: object): string => {
|
||||
if (Object.prototype.toString.call(source) === '[object Object]') {
|
||||
const paramsArr = <any>[];
|
||||
// 直接使用for in遍历对象ts会报错
|
||||
Object.entries(source).forEach(([prop, value]) => {
|
||||
if (typeof value === 'object') value = JSON.stringify(value);
|
||||
paramsArr.push(`${prop}=${value}`);
|
||||
});
|
||||
if (paramsArr.length > 0) return '?' + paramsArr.join('&');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="选择产品"
|
||||
style="width: 500px"
|
||||
@ok="handleOk"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="productId"
|
||||
style="width: 100%"
|
||||
:options="productList"
|
||||
>
|
||||
</a-select>
|
||||
<template #footer>
|
||||
<a-button key="back" @click="visible = false">取消</a-button>
|
||||
<a-button key="submit" type="primary" @click="handleOk"
|
||||
>确认</a-button
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { productItem } from '../../index';
|
||||
|
||||
const props = defineProps({
|
||||
openNumber: Number,
|
||||
});
|
||||
const emits = defineEmits(['confirm']);
|
||||
const { openNumber } = toRefs(props);
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
const productId = ref<string>('');
|
||||
const productList = ref<[productItem] | []>([]);
|
||||
|
||||
const getOptions = () => {
|
||||
productList.value = [];
|
||||
};
|
||||
const handleOk = () => {
|
||||
emits('confirm', productId.value);
|
||||
visible.value = false;
|
||||
};
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
watch(openNumber, () => {
|
||||
visible.value = true;
|
||||
productId.value = '';
|
||||
getOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,15 @@
|
|||
// 推荐步骤每项
|
||||
export interface recommendList {
|
||||
title: string;
|
||||
details: string;
|
||||
iconUrl: string;
|
||||
linkUrl: string;
|
||||
params?: object;
|
||||
auth: boolean;
|
||||
dialogTag?: 'accessMethod' | 'funcTest';
|
||||
}
|
||||
// 产品列表里的每项
|
||||
export interface productItem {
|
||||
label: string;
|
||||
value: string
|
||||
}
|
|
@ -4,7 +4,9 @@
|
|||
<div class="left"></div>
|
||||
<div class="content iot-home-container">
|
||||
<!-- <InitHome /> -->
|
||||
<DeviceHome />
|
||||
<!-- <DeviceHome /> -->
|
||||
<!-- <DevOpsHome /> -->
|
||||
<ComprehensiveHome />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -12,6 +14,9 @@
|
|||
<script lang="ts" setup>
|
||||
import InitHome from './components/InitHome/index.vue';
|
||||
import DeviceHome from './components/DeviceHome/index.vue';
|
||||
import DevOpsHome from './components/DevOpsHome/index.vue';
|
||||
import ComprehensiveHome from './components/ComprehensiveHome/index.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -30,14 +35,12 @@ import DeviceHome from './components/DeviceHome/index.vue';
|
|||
background: #352d85;
|
||||
width: 210px;
|
||||
min-height: calc(100vh - 48px);
|
||||
|
||||
}
|
||||
.content {
|
||||
margin: 24px;
|
||||
width: calc(100vw - 258px);
|
||||
width: calc(100vw - 280px);
|
||||
overflow: hidden;
|
||||
min-height: calc(100vh - 96px);
|
||||
}
|
||||
}
|
||||
// .iot-home-container {
|
||||
// }
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
const MetworkTypeMapping = new Map();
|
||||
MetworkTypeMapping.set('websocket-server', 'WEB_SOCKET_SERVER');
|
||||
MetworkTypeMapping.set('http-server-gateway', 'HTTP_SERVER');
|
||||
MetworkTypeMapping.set('udp-device-gateway', 'UDP');
|
||||
MetworkTypeMapping.set('coap-server-gateway', 'COAP_SERVER');
|
||||
MetworkTypeMapping.set('mqtt-client-gateway', 'MQTT_CLIENT');
|
||||
MetworkTypeMapping.set('mqtt-server-gateway', 'MQTT_SERVER');
|
||||
MetworkTypeMapping.set('tcp-server-gateway', 'TCP_SERVER');
|
||||
|
||||
const ProcotoleMapping = new Map();
|
||||
ProcotoleMapping.set('websocket-server', 'WebSocket');
|
||||
ProcotoleMapping.set('http-server-gateway', 'HTTP');
|
||||
ProcotoleMapping.set('udp-device-gateway', 'UDP');
|
||||
ProcotoleMapping.set('coap-server-gateway', 'CoAP');
|
||||
ProcotoleMapping.set('mqtt-client-gateway', 'MQTT');
|
||||
ProcotoleMapping.set('mqtt-server-gateway', 'MQTT');
|
||||
ProcotoleMapping.set('tcp-server-gateway', 'TCP');
|
||||
ProcotoleMapping.set('child-device', '');
|
||||
|
||||
const descriptionList = {
|
||||
'udp-device-gateway':
|
||||
'UDP可以让设备无需建立连接就可以与平台传输数据。在允许一定程度丢包的情况下,提供轻量化且简单的连接。',
|
||||
'tcp-server-gateway':
|
||||
'TCP服务是一种面向连接的、可靠的、基于字节流的传输层通信协议。设备可通过TCP服务与平台进行长链接,实时更新状态并发送消息。可自定义多种粘拆包规则,处理传输过程中可能发生的粘拆包问题。',
|
||||
'websocket-server':
|
||||
'WebSocket是一种在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据。设备通过WebSocket服务与平台进行长链接,实时更新状态并发送消息,且可以发布订阅消息',
|
||||
'mqtt-client-gateway':
|
||||
'MQTT是ISO 标准下基于发布/订阅范式的消息协议,具有轻量、简单、开放和易于实现的特点。平台使用指定的ID接入其他远程平台,订阅消息。也可添加用户名和密码校验。可设置最大消息长度。可统一设置共享的订阅前缀。',
|
||||
'http-server-gateway':
|
||||
'HTTP服务是一个简单的请求-响应的基于TCP的无状态协议。设备通过HTTP服务与平台进行灵活的短链接通信,仅支持设备和平台之间单对单的请求-响应模式',
|
||||
'mqtt-server-gateway':
|
||||
'MQTT是ISO 标准下基于发布/订阅范式的消息协议,具有轻量、简单、开放和易于实现的特点。提供MQTT的服务端,以供设备以长链接的方式接入平台。设备使用唯一的ID,也可添加用户名和密码校验。可设置最大消息长度。',
|
||||
'coap-server-gateway':
|
||||
'CoAP是针对只有少量的内存空间和有限的计算能力提供的一种基于UDP的协议。便于低功耗或网络受限的设备与平台通信,仅支持设备和平台之间单对单的请求-响应模式。',
|
||||
};
|
||||
|
||||
const columnsMQTT = [
|
||||
{
|
||||
title: '分组',
|
||||
dataIndex: 'group',
|
||||
key: 'group',
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
width: 100,
|
||||
scopedSlots: { customRender: 'group' },
|
||||
},
|
||||
{
|
||||
title: 'topic',
|
||||
dataIndex: 'topic',
|
||||
key: 'topic',
|
||||
scopedSlots: { customRender: 'topic' },
|
||||
},
|
||||
{
|
||||
title: '上下行',
|
||||
dataIndex: 'stream',
|
||||
key: 'stream',
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
width: 100,
|
||||
scopedSlots: { customRender: 'stream' },
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
scopedSlots: { customRender: 'description' },
|
||||
},
|
||||
]
|
||||
|
||||
const columnsHTTP = [
|
||||
{
|
||||
title: '分组',
|
||||
dataIndex: 'group',
|
||||
key: 'group',
|
||||
ellipsis: true,
|
||||
width: 100,
|
||||
scopedSlots: { customRender: 'group' },
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
scopedSlots: { customRender: 'address' },
|
||||
},
|
||||
{
|
||||
title: '示例',
|
||||
dataIndex: 'example',
|
||||
key: 'example',
|
||||
scopedSlots: { customRender: 'example' },
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
scopedSlots: { customRender: 'description' }
|
||||
},
|
||||
]
|
||||
|
||||
export { MetworkTypeMapping, ProcotoleMapping, descriptionList, columnsMQTT, columnsHTTP };
|
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
<a-card :bordered="false">
|
||||
<TitleComponent data="自定义设备接入"></TitleComponent>
|
||||
<div>
|
||||
<a-row :gutter="[24, 24]">
|
||||
<a-col :span="12" v-for="item in items" :key="item.id">
|
||||
<div class="provider">
|
||||
<div class="box">
|
||||
<div class="left">
|
||||
<div class="images">
|
||||
<img :src="backMap.get(item.id)" />
|
||||
</div>
|
||||
<div class="context">
|
||||
<div class="title">{{ item.name }}</div>
|
||||
<div class="desc">
|
||||
<a-tooltip :title="item.description">
|
||||
{{ item.description || '' }}
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="goProviders(item)"
|
||||
>接入</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="AccessConfigDetail">
|
||||
import { getImage } from '@/utils/comm';
|
||||
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||
|
||||
const items = [
|
||||
{ id: 'mqtt-server-gateway', name: '测试1', description: '测试1' },
|
||||
{ id: 'websocket-server', name: '测试2', description: '测试' },
|
||||
{ id: 'coap-server-gateway', name: '测试3', description: '测试' },
|
||||
];
|
||||
|
||||
const backMap = new Map();
|
||||
backMap.set('mqtt-server-gateway', getImage('/access/mqtt.png'));
|
||||
backMap.set('websocket-server', getImage('/access/websocket.png'));
|
||||
backMap.set('coap-server-gateway', getImage('/access/coap.png'));
|
||||
backMap.set('tcp-server-gateway', getImage('/access/tcp.png'));
|
||||
backMap.set('child-device', getImage('/access/child-device.png'));
|
||||
backMap.set('http-server-gateway', getImage('/access/http.png'));
|
||||
backMap.set('udp-device-gateway', getImage('/access/udp.png'));
|
||||
backMap.set('mqtt-client-gateway', getImage('/access/mqtt-broke.png'));
|
||||
|
||||
const goProviders = (value: object) => {
|
||||
console.log(111, value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.provider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
background: url('/public/images/access/background.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
border: 1px solid #e6e6e6;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 40px;
|
||||
display: block;
|
||||
width: 15%;
|
||||
min-width: 64px;
|
||||
height: 2px;
|
||||
background-image: url('/public/images/access/rectangle.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
// border: 1px #8da1f4 solid;
|
||||
// border-bottom-left-radius: 10%;
|
||||
// border-bottom-right-radius: 10%;
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 24px rgba(#000, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
width: calc(100% - 70px);
|
||||
.images {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.context {
|
||||
width: calc(100% - 84px);
|
||||
margin: 10px;
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.desc {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<a-button type="primary" @click="handlAdd">新增</a-button>
|
||||
</template>
|
||||
<script lang="ts" setup name="AccessConfigPage">
|
||||
|
||||
const handlAdd = (e: any) => {
|
||||
console.log(111,e);
|
||||
|
||||
}
|
||||
|
||||
</script>
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
<div>
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
@change="textChange"
|
||||
v-model="keystoreBase64"
|
||||
:placeholder=placeholder
|
||||
/>
|
||||
<a-upload
|
||||
accept=".pem"
|
||||
listType="text"
|
||||
:action="action"
|
||||
:headers="headers"
|
||||
:showUploadList="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-button style="margin-top: 10px">
|
||||
<upload-outlined></upload-outlined>
|
||||
|
||||
上传文件</a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import storage from 'store'
|
||||
// import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
// import { ACCESS_TOKEN_KEY } from '@/utils/consts'
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
name: 'CertificateFile',
|
||||
data () {
|
||||
return {
|
||||
keystoreBase64: '',
|
||||
loading: false,
|
||||
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
|
||||
headers:{
|
||||
authorization: 'authorization-text',
|
||||
}
|
||||
// action: process.env.VUE_APP_BASE_API + `/network/certificate/upload`,
|
||||
// headers: {
|
||||
// [ACCESS_TOKEN_KEY]: storage.get(ACCESS_TOKEN)
|
||||
// }
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler (v) {
|
||||
this.keystoreBase64 = v
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (info) {
|
||||
this.loading = true
|
||||
if (info.file.status === 'done') {
|
||||
this.$message.success('上传成功!')
|
||||
const result = info.file.response?.result
|
||||
this.loading = false
|
||||
this.$emit('change', result)
|
||||
}
|
||||
},
|
||||
textChange (val) {
|
||||
this.$emit('change', val)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
|
@ -0,0 +1,207 @@
|
|||
<template>
|
||||
<a-card>
|
||||
<a-row :gutter="[24, 24]" style="padding: 24px">
|
||||
<a-col :span="12">
|
||||
<a-form
|
||||
class="form"
|
||||
layout="vertical"
|
||||
:model="formData"
|
||||
name="basic"
|
||||
:label-col="{ span: 8 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
autocomplete="off"
|
||||
@finish="onFinish"
|
||||
@finishFailed="onFinishFailed"
|
||||
>
|
||||
<a-form-item
|
||||
label="证书标准"
|
||||
name="type"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请选择证书标准',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a-radio-group v-model:value="formData.type">
|
||||
<a-radio-button
|
||||
class="form-radio-button"
|
||||
value="common"
|
||||
>
|
||||
<img :src="getImage('/certificate.png')" />
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="证书名称"
|
||||
name="name"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入证书名称',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
placeholder="请输入证书名称"
|
||||
v-model:value="formData.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="证书文件"
|
||||
name="cert"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '上传证书文件',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<CertificateFile
|
||||
placeholder='证书格式以"-----BEGIN CERTIFICATE-----"开头,以"-----END CERTIFICATE-----"结尾"'
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="证书私钥"
|
||||
name="key"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请上传证书私钥',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<CertificateFile
|
||||
placeholder='证书私钥格式以"-----BEGIN (RSA|EC) PRIVATE KEY-----"开头,以"-----END(RSA|EC) PRIVATE KEY-----"结尾。'
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="说明" name="description">
|
||||
<a-textarea
|
||||
placeholder="请输入说明"
|
||||
v-model:value="formData.description"
|
||||
:maxlength="200"
|
||||
:rows="3"
|
||||
showCount
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button
|
||||
class="form-submit"
|
||||
html-type="submit"
|
||||
type="primary"
|
||||
>保存</a-button
|
||||
>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="doc">
|
||||
<h1>1. 概述</h1>
|
||||
<div>
|
||||
证书由受信任的数字证书颁发机构CA,在验证服务器身份后颁发,具有服务器身份验证和数据传输加密功能,保障设备与平台间的数据传输安全。配置后可被网络组件引用。
|
||||
</div>
|
||||
<h1>2. 配置说明</h1>
|
||||
<h2>1、证书文件</h2>
|
||||
<div>
|
||||
您可以使用文本编辑工具打开PEM或者CRT格式的证书文件,复制其中的内容并粘贴到该文本框,或者单击该文本框下的上传,并选择存储在本地计算机的证书文件,将文件内容上传到文本框。
|
||||
</div>
|
||||
<h2>2、证书私钥</h2>
|
||||
<div>
|
||||
填写证书私钥内容的PEM编码。
|
||||
您可以使用文本编辑工具打开KEY格式的证书私钥文件,复制其中的内容并粘贴到该文本框,或者单击该文本框下的上传并选择存储在本地计算机的证书私钥文件,将文件内容上传到文本框。
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</template>
|
||||
<!-- export const ACCESS_TOKEN_KEY = 'X-Access-Token' -->
|
||||
<!-- export const ACCESS_TOKEN = 'device_token' -->
|
||||
|
||||
<script lang="ts" setup name="CertificateDetail">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import CertificateFile from './CertificateFile.vue';
|
||||
|
||||
const formData = reactive({
|
||||
type: 'common',
|
||||
name: '',
|
||||
configs: {
|
||||
cert: '',
|
||||
key: '',
|
||||
},
|
||||
description: '',
|
||||
});
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
console.log('Success:', values);
|
||||
};
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
|
||||
const headers = {
|
||||
authorization: 'authorization-text',
|
||||
};
|
||||
|
||||
const handleChange = (info: any) => {
|
||||
if (info.file.status !== 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
message.success(`${info.file.name} file uploaded successfully`);
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error(`${info.file.name} file upload failed.`);
|
||||
}
|
||||
};
|
||||
const fileList = ref([]);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.form {
|
||||
.form-radio-button {
|
||||
width: 148px;
|
||||
height: 80px;
|
||||
padding: 0;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.form-upload-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.form-submit {
|
||||
background-color: @primary-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
.doc {
|
||||
height: 100%;
|
||||
padding: 0 24px;
|
||||
overflow-y: auto;
|
||||
color: rgba(#000, 0.8);
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
|
||||
h1 {
|
||||
margin: 16px 0;
|
||||
color: rgba(#000, 0.85);
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 6px 10px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<a-button type="primary" @click="handlAdd">新增</a-button>
|
||||
</template>
|
||||
<script lang="ts" setup name="CertificatePage">
|
||||
|
||||
const handlAdd = (e: any) => {
|
||||
console.log(111,e);
|
||||
|
||||
}
|
||||
|
||||
</script>
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<div>访问日志</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="SystemLog">
|
||||
|
||||
</script>
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<div>系统日志</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="AccessLog">
|
||||
|
||||
</script>
|
|
@ -0,0 +1,17 @@
|
|||
<template>
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="1" tab="访问日志">
|
||||
<AccessLog />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="系统日志" force-render>
|
||||
<SystemLog />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
<script lang="ts" setup name="LogPage">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import AccessLog from './Access/index.vue';
|
||||
import SystemLog from './System/index.vue';
|
||||
|
||||
const activeKey = ref('1');
|
||||
</script>
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div class="box">
|
||||
<JTable
|
||||
:columns="[
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '年龄',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
},
|
||||
{
|
||||
title: '住址',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
}
|
||||
]"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-button type="primary">新增</a-button>
|
||||
</template>
|
||||
<template #cardRender="slotProps">
|
||||
{{slotProps.name}}
|
||||
</template>
|
||||
</JTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { post } from "@/utils/request";
|
||||
// :request="post('/device-product/_query', {})"
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" scoped>
|
||||
.box {
|
||||
padding: 20px;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,534 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-spin :spinning="loading" :delay="500">
|
||||
<div class="container">
|
||||
<div class="left">
|
||||
<img
|
||||
style="width: 100%; height: 100%"
|
||||
:src="getImage('/login.png')"
|
||||
/>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="lang" data-lang=""></div>
|
||||
<div class="content">
|
||||
<div class="top">
|
||||
<div class="header">
|
||||
<!-- <link to="/"> -->
|
||||
<img
|
||||
alt="logo"
|
||||
class="logo"
|
||||
:src="getImage('/logo.png')"
|
||||
/>
|
||||
<!-- </link> -->
|
||||
</div>
|
||||
<div class="desc">物联网平台</div>
|
||||
<div class="main">
|
||||
<a-form
|
||||
layout="vertical"
|
||||
:model="form"
|
||||
class="login-form"
|
||||
@finish="onFinish"
|
||||
>
|
||||
<a-form-item
|
||||
label="账号"
|
||||
name="username"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入账号!',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="form.username"
|
||||
placeholder="请输入账号"
|
||||
:maxlength="64"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码"
|
||||
name="password"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密码!',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a-input-password
|
||||
v-model:value="form.password"
|
||||
placeholder="请输入密码"
|
||||
:maxlength="64"
|
||||
></a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="codeConfig"
|
||||
class="verifyCode"
|
||||
label="验证码"
|
||||
name="verifyCode"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入验证码!',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
class="login-code-input"
|
||||
v-model:value="form.verifyCode"
|
||||
autocomplete="off"
|
||||
:maxlength="64"
|
||||
placeholder="请输入验证码"
|
||||
></a-input>
|
||||
<div class="login-code">
|
||||
<img
|
||||
:src="codeUrl"
|
||||
@click="getCode()"
|
||||
class="login-code-img"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
name="remember"
|
||||
style="text-align: left"
|
||||
>
|
||||
<a-checkbox
|
||||
v-model:checked="form.remember"
|
||||
>记住密码</a-checkbox
|
||||
>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button
|
||||
:loading="loading"
|
||||
type="primary"
|
||||
html-type="submit"
|
||||
class="login-form-button"
|
||||
block
|
||||
>
|
||||
登录
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div style="margin-top: 20px">
|
||||
<a-divider plain style="height: 12px">
|
||||
<div
|
||||
style="color: #807676d9, font-size: 12px"
|
||||
>
|
||||
其他方式登录
|
||||
</div>
|
||||
</a-divider>
|
||||
<div
|
||||
style="position: relative, bottom: 10px; text-align: center"
|
||||
>
|
||||
<a-button
|
||||
v-for="(item, index) in bindings"
|
||||
:key="index"
|
||||
type="link"
|
||||
@Click="handleClickOther(item)"
|
||||
>
|
||||
<img
|
||||
style="width: 32px, height: 33px"
|
||||
:alt="item.name"
|
||||
:src="
|
||||
iconMap.get(
|
||||
item.provider,
|
||||
) || defaultImg
|
||||
"
|
||||
/>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="view">
|
||||
JETLINKS团队全新力作可视化大屏系统
|
||||
</div>
|
||||
<div class="url">
|
||||
<div style="height: 33px">
|
||||
<img :src="viewLogo" />
|
||||
</div>
|
||||
<a
|
||||
href="https://view.jetlinks.cn/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
体验DEMO
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getImage } from '@/utils/comm';
|
||||
import {
|
||||
config,
|
||||
code,
|
||||
authLogin,
|
||||
getInitSet,
|
||||
systemVersion,
|
||||
bindInfo,
|
||||
} from '@/api/login';
|
||||
import Cookies from 'js-cookie';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
import { LocalStore } from '@/utils/comm';
|
||||
import { BASE_API_PATH, TOKEN_KEY, Version_Code } from '@/utils/variable';
|
||||
|
||||
const store = useUserInfo();
|
||||
const router = useRouter();
|
||||
const bgImage = getImage('/logo.png');
|
||||
const viewLogo = getImage('/view-logo.png');
|
||||
|
||||
const LoginWarpStyle = reactive({
|
||||
backgroundImage: `url(${bgImage})`,
|
||||
});
|
||||
|
||||
const screenWidth = ref(document.body.clientWidth);
|
||||
const screenHeight = ref(document.body.clientHeight);
|
||||
|
||||
const form = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
remember: false,
|
||||
expires: 3600000,
|
||||
verifyCode: '',
|
||||
verifyKey: '',
|
||||
});
|
||||
|
||||
const codeUrl = ref('');
|
||||
const codeConfig = ref(false);
|
||||
|
||||
const loading = ref(false);
|
||||
const bindings = ref<any[]>();
|
||||
|
||||
const defaultImg = getImage('/apply/provider1.png');
|
||||
const iconMap = new Map();
|
||||
iconMap.set('dingtalk-ent-app', getImage('/bind/dingtalk.png'));
|
||||
iconMap.set('wechat-webapp', getImage('/bind/wechat-webapp.png'));
|
||||
|
||||
const onFinish = async () => {
|
||||
form.remember
|
||||
? Cookies.set('user', encodeURIComponent(JSON.stringify(form)), {
|
||||
expires: 7,
|
||||
})
|
||||
: Cookies.remove('user');
|
||||
Cookies.set('username', form.username, { expires: 30 });
|
||||
try {
|
||||
loading.value = true;
|
||||
const res: any = await authLogin(form);
|
||||
if (res.success) {
|
||||
store.$patch({
|
||||
...res.result,
|
||||
username: form.username,
|
||||
});
|
||||
LocalStore.set(TOKEN_KEY, res?.result.token);
|
||||
const resp: any = await getInitSet();
|
||||
if (resp.success) {
|
||||
router.push('/demo');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
form.verifyCode = '';
|
||||
getCode();
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getCode = async () => {
|
||||
const configRes: any = await config();
|
||||
if (!configRes.success || (configRes.success && !configRes.result.enabled))
|
||||
return;
|
||||
|
||||
codeConfig.value = true;
|
||||
const res: any = await code();
|
||||
if (res.success) {
|
||||
codeUrl.value = res.result.base64;
|
||||
form.verifyKey = res.result.key;
|
||||
}
|
||||
};
|
||||
|
||||
const getCookie = () => {
|
||||
// form.username = Cookies.get('username');
|
||||
if (!Cookies.get('user')) return;
|
||||
const user = JSON.parse(decodeURIComponent(Cookies.get('user')));
|
||||
form.username = user.username;
|
||||
form.password = user.password;
|
||||
form.remember = user.remember || false;
|
||||
};
|
||||
|
||||
const getOpen = () => {
|
||||
systemVersion().then((res: any) => {
|
||||
if (res.success && res.result) {
|
||||
LocalStore.set(Version_Code, res.result.edition);
|
||||
if (res.result.edition !== 'community') {
|
||||
bindInfo().then((res: any) => {
|
||||
if (res.success) {
|
||||
bindings.value = res.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleClickOther = (item: any) => {
|
||||
LocalStore.set('onLogin', 'no');
|
||||
window.open(`${BASE_API_PATH}/application/sso/${item.id}/login`);
|
||||
window.onstorage = (e) => {
|
||||
if (e.newValue) {
|
||||
window.location.href = '/';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const screenRotation = (width: number, height: number) => {
|
||||
LoginWarpStyle.backgroundImage = `url(${bgImage})`;
|
||||
};
|
||||
|
||||
window.onresize = () => {
|
||||
return (() => {
|
||||
screenWidth.value = document.body.clientWidth;
|
||||
screenHeight.value = document.body.clientHeight;
|
||||
})();
|
||||
};
|
||||
|
||||
watch(
|
||||
[() => screenWidth.value, () => screenHeight.value],
|
||||
(value) => {
|
||||
screenRotation(value[0], value[1]);
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
getOpen();
|
||||
getCode();
|
||||
getCookie();
|
||||
screenRotation(screenWidth.value, screenHeight.value);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@import 'ant-design-vue/es/style/themes/default.less';
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
background: @layout-body-background;
|
||||
|
||||
.left {
|
||||
width: 73%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 27%;
|
||||
background: #fff;
|
||||
|
||||
:deep(.ant-layout-footer) {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.lang {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 44px;
|
||||
text-align: right;
|
||||
|
||||
:global(.ant-dropdown-trigger) {
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: center;
|
||||
padding: 0 0 15% 0;
|
||||
|
||||
.top {
|
||||
width: 100%;
|
||||
// text-align: center;
|
||||
|
||||
.header {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 44px;
|
||||
margin-right: 16px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
color: @heading-color;
|
||||
font-weight: 600;
|
||||
font-size: 33px;
|
||||
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica,
|
||||
sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 40px;
|
||||
// color: @heading-color;
|
||||
color: rgb(0 0 0 / 70%);
|
||||
font-weight: 600;
|
||||
font-size: 22px;
|
||||
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica,
|
||||
sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 70%;
|
||||
margin: 60px auto 0;
|
||||
|
||||
@media screen and (max-width: @screen-sm) {
|
||||
width: 95%;
|
||||
max-width: 328px;
|
||||
}
|
||||
|
||||
// ::v-deep {
|
||||
// .@{ant-prefix}-tabs-nav-list {
|
||||
// margin: auto;
|
||||
// font-size: 16px;
|
||||
// }
|
||||
|
||||
// // .ant-formily-item-size-large .ant-formily-item-help {
|
||||
// // text-align: left;
|
||||
// // }
|
||||
// }
|
||||
|
||||
.icon {
|
||||
margin-left: 16px;
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
font-size: 24px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
margin-top: 24px;
|
||||
line-height: 22px;
|
||||
text-align: left;
|
||||
|
||||
.register {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.prefixIcon {
|
||||
color: @primary-color;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
|
||||
.remember {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.verifyCode {
|
||||
.login-code-input {
|
||||
width: 70%;
|
||||
float: left;
|
||||
}
|
||||
.login-code {
|
||||
width: 30%;
|
||||
height: 32px;
|
||||
float: left;
|
||||
background-color: #e4e6e7;
|
||||
img {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.login-code-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 11%;
|
||||
border-top: 1px solid #e0e4e8;
|
||||
|
||||
.view {
|
||||
width: 247px;
|
||||
height: 20px;
|
||||
margin-right: 10%;
|
||||
margin-bottom: 10px;
|
||||
padding-top: 2px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 14px;
|
||||
font-family: 'PingFang SC';
|
||||
}
|
||||
|
||||
.url {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 10%;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
left: 60px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2px 7px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #2f54eb;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: @screen-md-min) {
|
||||
.container {
|
||||
//background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 110px;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 32px 0 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,6 @@
|
|||
import {defineConfig, loadEnv} from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import {AntDesignVueResolver} from 'unplugin-vue-components/resolvers'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
|
@ -46,6 +47,7 @@ export default defineConfig(({ mode}) => {
|
|||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
Components({
|
||||
resolvers: [AntDesignVueResolver({ importStyle: 'less' }), VueAmapResolver()],
|
||||
directoryAsNamespace: true
|
||||
|
@ -74,9 +76,10 @@ export default defineConfig(({ mode}) => {
|
|||
[env.VITE_APP_BASE_API]: {
|
||||
// target: 'http://192.168.33.22:8800',
|
||||
// target: 'http://192.168.32.244:8881',
|
||||
target: 'http://47.112.135.104:5096', // opcua
|
||||
// target: 'http://47.112.135.104:5096', // opcua
|
||||
target: 'http://47.108.63.174:8845', // 测试
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace('^'+env.VITE_APP_BASE_API, '')
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue