This commit is contained in:
六如
2024-12-01 13:10:32 +08:00
parent 09330a7431
commit c0cfdbafd9
22 changed files with 1584 additions and 513 deletions

View File

@@ -78,6 +78,7 @@ public class DocInfoSyncService {
docInfo.setDocTitle(tornaDocInfoDTO.getName()); docInfo.setDocTitle(tornaDocInfoDTO.getName());
docInfo.setDocCode(""); docInfo.setDocCode("");
if (YesOrNo.yes(tornaDocInfoDTO.getIsFolder())) { if (YesOrNo.yes(tornaDocInfoDTO.getIsFolder())) {
docInfo.setIsPublish(YesOrNo.YES);
docInfo.setDocName(tornaDocInfoDTO.getName()); docInfo.setDocName(tornaDocInfoDTO.getName());
} }
docInfo.setDocId(tornaDocInfoDTO.getId()); docInfo.setDocId(tornaDocInfoDTO.getId());

View File

@@ -25,7 +25,7 @@ public class TornaDocInfoViewDTO {
private String description; private String description;
/** /**
* 0:http,1:dubbo * 0:http,1:dubbo,2:富文本,3:Markdown
*/ */
private Byte type; private Byte type;

View File

@@ -169,7 +169,7 @@ public class TornaDocInfoViewVO {
private List<TornaDocInfoViewVO> children = Collections.emptyList(); private List<TornaDocInfoViewVO> children = Collections.emptyList();
public String getDocName() { public String getDocName() {
return name; return url;
} }
public String getDocTitle() { public String getDocTitle() {

View File

@@ -8,7 +8,7 @@
name="viewport" name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/> />
<title>pure-admin-thin</title> <title>XX开放平台</title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<script> <script>
window.process = {}; window.process = {};

View File

@@ -20,6 +20,14 @@ const permissionRouter = {
meta: { meta: {
title: "API文档" title: "API文档"
} }
},
{
path: "/doc/sign",
name: "DocSign",
meta: {
title: "签名算法",
showLink: false
}
} }
] ]
}; };

View File

@@ -1,5 +1,5 @@
{ {
"name": "pure-admin-thin", "name": "XX开放平台",
"version": "5.8.0", "version": "5.8.0",
"private": true, "private": true,
"type": "module", "type": "module",
@@ -48,95 +48,100 @@
"url": "https://github.com/xiaoxian521" "url": "https://github.com/xiaoxian521"
}, },
"dependencies": { "dependencies": {
"@pureadmin/descriptions": "^1.2.1", "@bytemd/plugin-gemoji": "^1.21.0",
"@pureadmin/table": "^3.2.0", "@bytemd/plugin-gfm": "^1.21.0",
"@pureadmin/utils": "^2.4.8", "@bytemd/plugin-highlight": "^1.21.0",
"@vueuse/core": "^10.11.1", "@bytemd/vue-next": "^1.21.0",
"@vueuse/motion": "^2.2.3", "@pureadmin/descriptions": "1.2.1",
"animate.css": "^4.1.1", "@pureadmin/table": "3.2.0",
"axios": "^1.7.4", "@pureadmin/utils": "2.4.8",
"dayjs": "^1.11.12", "@vueuse/core": "10.11.1",
"echarts": "^5.5.1", "@vueuse/motion": "2.2.3",
"element-plus": "^2.8.0", "animate.css": "4.1.1",
"js-cookie": "^3.0.5", "axios": "1.7.4",
"localforage": "^1.10.0", "dayjs": "1.11.12",
"mavon-editor": "2.7.7", "echarts": "5.5.1",
"mitt": "^3.0.1", "element-plus": "2.8.0",
"nprogress": "^0.2.0", "github-markdown-css": "^5.8.1",
"path": "^0.12.7", "highlight.js": "^11.10.0",
"pinia": "^2.2.2", "js-cookie": "3.0.5",
"pinyin-pro": "^3.24.2", "localforage": "1.10.0",
"qs": "^6.13.0", "mitt": "3.0.1",
"responsive-storage": "^2.2.0", "nprogress": "0.2.0",
"sortablejs": "^1.15.2", "path": "0.12.7",
"vue": "^3.4.38", "pinia": "2.2.2",
"vue-i18n": "^9.14.0", "pinyin-pro": "3.24.2",
"vue-router": "^4.4.3", "qs": "6.13.0",
"vue-tippy": "^6.4.4", "responsive-storage": "2.2.0",
"vue-types": "^5.1.3" "sortablejs": "1.15.2",
"vue": "3.4.38",
"vue-i18n": "9.14.0",
"vue-router": "4.4.3",
"vue-tippy": "6.4.4",
"vue-types": "5.1.3"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.4.0", "@commitlint/cli": "19.4.0",
"@commitlint/config-conventional": "^19.2.2", "@commitlint/config-conventional": "19.2.2",
"@commitlint/types": "^19.0.3", "@commitlint/types": "19.0.3",
"@eslint/js": "^9.9.0", "@eslint/js": "9.9.0",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "8.4.1",
"@iconify-icons/ep": "^1.2.12", "@iconify-icons/ep": "1.2.12",
"@iconify-icons/ri": "^1.2.10", "@iconify-icons/ri": "1.2.10",
"@iconify/vue": "^4.1.2", "@iconify/vue": "4.1.2",
"@intlify/unplugin-vue-i18n": "^4.0.0", "@intlify/unplugin-vue-i18n": "4.0.0",
"@pureadmin/theme": "^3.2.0", "@pureadmin/theme": "3.2.0",
"@types/gradient-string": "^1.1.6", "@types/gradient-string": "1.1.6",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "3.0.6",
"@types/node": "^20.16.1", "@types/node": "20.16.1",
"@types/nprogress": "^0.2.3", "@types/nprogress": "0.2.3",
"@types/qs": "^6.9.15", "@types/qs": "6.9.15",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "1.15.8",
"@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "^7.18.0", "@typescript-eslint/parser": "7.18.0",
"@vitejs/plugin-vue": "^5.1.2", "@vitejs/plugin-vue": "5.1.2",
"@vitejs/plugin-vue-jsx": "^4.0.1", "@vitejs/plugin-vue-jsx": "4.0.1",
"autoprefixer": "^10.4.20", "autoprefixer": "10.4.20",
"boxen": "^7.1.1", "boxen": "7.1.1",
"cssnano": "^7.0.5", "cssnano": "7.0.5",
"eslint": "^9.9.0", "eslint": "9.9.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "9.1.0",
"eslint-define-config": "^2.1.0", "eslint-define-config": "2.1.0",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "5.2.1",
"eslint-plugin-vue": "^9.27.0", "eslint-plugin-vue": "9.27.0",
"gradient-string": "^2.0.2", "gradient-string": "2.0.2",
"husky": "^9.1.4", "husky": "9.1.4",
"lint-staged": "^15.2.9", "lint-staged": "15.2.9",
"postcss": "^8.4.41", "postcss": "8.4.41",
"postcss-html": "^1.7.0", "postcss-html": "1.7.0",
"postcss-import": "^16.1.0", "postcss-import": "16.1.0",
"postcss-scss": "^4.0.9", "postcss-scss": "4.0.9",
"prettier": "^3.3.3", "prettier": "3.3.3",
"rimraf": "^5.0.10", "rimraf": "5.0.10",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "5.12.0",
"sass": "^1.77.8", "sass": "1.77.8",
"stylelint": "^16.8.2", "stylelint": "16.8.2",
"stylelint-config-recess-order": "^5.0.1", "stylelint-config-recess-order": "5.0.1",
"stylelint-config-recommended-vue": "^1.5.0", "stylelint-config-recommended-vue": "1.5.0",
"stylelint-config-standard-scss": "^13.1.0", "stylelint-config-standard-scss": "13.1.0",
"stylelint-prettier": "^5.0.2", "stylelint-prettier": "5.0.2",
"svgo": "^3.3.2", "svgo": "3.3.2",
"tailwindcss": "^3.4.10", "tailwindcss": "3.4.10",
"typescript": "^5.5.4", "typescript": "5.5.4",
"vite": "^5.4.1", "vite": "5.4.1",
"vite-plugin-cdn-import": "^1.0.1", "vite-plugin-cdn-import": "1.0.1",
"vite-plugin-checker": "^0.7.2", "vite-plugin-checker": "0.7.2",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "0.5.1",
"vite-plugin-fake-server": "^2.1.1", "vite-plugin-fake-server": "2.1.1",
"vite-plugin-remove-console": "^2.2.0", "vite-plugin-remove-console": "2.2.0",
"vite-plugin-router-warn": "^1.0.0", "vite-plugin-router-warn": "1.0.0",
"vite-plugin-vue-inspector": "^5.1.3", "vite-plugin-vue-inspector": "5.1.3",
"vite-svg-loader": "^5.1.0", "vite-svg-loader": "5.1.0",
"vue-eslint-parser": "^9.4.3", "vue-eslint-parser": "9.4.3",
"vue-tsc": "^2.0.29" "vue-tsc": "2.0.29"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0", "node": "18.18.0 || ^20.9.0 || >=21.1.0",
"pnpm": ">=9" "pnpm": ">=9"
}, },
"pnpm": { "pnpm": {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
# 签名算法
签名步骤如下:
1.筛选并排序
获取所有请求参数不包括字节类型参数如文件、字节流剔除sign字段剔除值为空的参数并按照参数名ASCII码递增排序字母升序排序如果遇到相同字符则按照第二个字符的键值ASCII码递增排序以此类推。
2.拼接
将排序后的参数与其对应值,组合成“参数=参数值”的格式,并且把这些参数用&字符连接起来,此时生成的字符串为待签名字符串。
3.调用签名函数
使用各自语言对应的`SHA256WithRSA`签名函数并使用应用私钥对待签名字符串进行签名对结果Base64编码。
4.把生成的签名赋值给`sign`参数,拼接到请求参数中。
- 示例
假设目前已经存在一个接口:获取会员信息
- 接口名member.info.get
- 版本号1.0
- 请求方式GET
- 请求参数name=jim&age=123&address=xxx
它的业务参数为:
```
name=jim
age=22
address=xx
```
加上公共请求参数:
```
app_id=test_2020050924567817013559296
method=member.info.get
version=1.0
charset=UTF-8
timestamp=2019-06-03 15:18:29
sign=(待生成)
```
把业务请求参数和公共请求参数加起来然后按照参数名ASCII码递增排序
则待签名字符串为:
```
address=xx&age=22&app_id=test_2020050924567817013559296&charset=UTF-8&method=member.info.get&name=jim&timestamp=2020-06-03 15:23:30&version=1.0
```
使用各自语言对应的`SHA256WithRSA`签名函数并使用`应用私钥`对待签名字符串进行签名对结果Base64编码得到字符串`adfdxadsf3asdfa`
把该字符串给`sign`参数,拼接到请求参数中,得到最终请求参数为:
```
name=jim
age=22
address=xx
app_id=test_2020050924567817013559296
method=member.info.get
version=1.0
charset=UTF-8
timestamp=2019-06-03 15:18:29
sign=adfdxadsf3asdfa
```
如果开放平台已经提供SDK那么SDK中已经封装好签名步骤直接调用SDK中的方法即可完成接口请求。

View File

@@ -0,0 +1,93 @@
---
# frontmatter: https://jekyllrb.com/docs/front-matter/
layout: post
title: Blogging Like a Hacker
---
## Markdown Basic Syntax
I just love **bold text**. Italicized text is the _cat's meow_. At the command prompt, type `nano`.
My favorite markdown editor is [ByteMD](https://github.com/bytedance/bytemd).
1. First item
2. Second item
3. Third item
a
> Dorothy followed her through many of the beautiful rooms in her castle.
```js
import gfm from '@bytemd/plugin-gfm'
import { Editor, Viewer } from 'bytemd'
const plugins = [
gfm(),
// Add more plugins here
]
const editor = new Editor({
target: document.body, // DOM to render
props: {
value: '',
plugins,
},
})
editor.on('change', (e) => {
editor.$set({ value: e.detail.value })
})
```
## GFM Extended Syntax
Automatic URL Linking: https://github.com/bytedance/bytemd
~~The world is flat.~~ We now know that the world is round.
- [x] Write the press release
- [ ] Update the website
- [ ] Contact the media
| Syntax | Description |
| --------- | ----------- |
| Header | Title |
| Paragraph | Text |
## Footnotes
Here's a simple footnote,[^1] and here's a longer one.[^bignote]
[^1]: This is the first footnote.
[^bignote]: Here's one with multiple paragraphs and code.
Indent paragraphs to include them in the footnote.
`{ my code }`
Add as many paragraphs as you like.
## Gemoji
Thumbs up: :+1:, thumbs down: :-1:.
Families: :family_man_man_boy_boy:
Long flags: :wales:, :scotland:, :england:.
## Math Equation
Inline math equation: $a+b$
$$
\displaystyle \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)
$$
## Mermaid Diagrams
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```

View File

@@ -0,0 +1,6 @@
import mdView from "./index.vue";
import { withInstall } from "@pureadmin/utils";
const MarkdownEditor = withInstall(mdView);
export default MarkdownEditor;

View File

@@ -0,0 +1,83 @@
<script setup lang="ts">
// bytemd https://github.com/bytedance/bytemd.git
import { Editor, Viewer } from "@bytemd/vue-next";
import gfm from "@bytemd/plugin-gfm";
import highlight from "@bytemd/plugin-highlight";
import gemoji from "@bytemd/plugin-gemoji";
import "bytemd/dist/index.css";
import "highlight.js/styles/idea.css";
import "github-markdown-css/github-markdown.css";
import { computed, ref } from "vue";
const plugins = [gfm(), highlight(), gemoji()];
const props = defineProps({
value: {
type: String,
default: "",
required: true
},
readonly: {
type: Boolean,
default: false
}
});
const updateValue = ref("");
const content = computed(() => {
return updateValue.value || props.value;
});
const isEdit = computed(() => {
return !props.readonly;
});
function handleChange(v) {
updateValue.value = v;
}
</script>
<template>
<div>
<Editor
v-if="isEdit"
:value="content"
:plugins="plugins"
@change="handleChange"
/>
<Viewer v-else :value="content" :plugins="plugins" />
</div>
</template>
<style lang="scss">
.bytemd {
height: calc(100vh - 100px);
}
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 25px;
ol {
padding-left: 1em;
margin: 0;
list-style: decimal !important;
}
ul {
padding-left: 1em;
margin: 0;
list-style: disc !important;
}
}
.markdown-body p,
.markdown-body blockquote,
.markdown-body ul,
.markdown-body ol,
.markdown-body dl,
.markdown-body table,
.markdown-body pre,
.markdown-body details {
margin-top: 0;
margin-bottom: 16px;
}
</style>

View File

@@ -22,8 +22,6 @@ import "element-plus/dist/index.css";
// 导入字体图标 // 导入字体图标
import "./assets/iconfont/iconfont.js"; import "./assets/iconfont/iconfont.js";
import "./assets/iconfont/iconfont.css"; import "./assets/iconfont/iconfont.css";
import "mavon-editor/dist/css/index.css";
const app = createApp(App); const app = createApp(App);
// 自定义指令 // 自定义指令

View File

@@ -0,0 +1,5 @@
export enum DocType {
DOC = 1,
RICH_TEXT = 2,
MARKDOWN = 3
}

View File

@@ -32,10 +32,14 @@
.api-description { .api-description {
font-size: 14px; font-size: 14px;
a { a {
color: #406eeb; color: #409eff;
} }
} }
.is-bordered-label { .is-bordered-label {
width: 200px !important; width: 200px !important;
} }
} }
.sop-link {
color: #409eff;
}

View File

@@ -216,3 +216,13 @@ class PureHttp {
} }
export const http = new PureHttp(); export const http = new PureHttp();
/**
* 文件必须放在public下面
* @param path 相对于public文件夹路径如文件在public/static/sign.mdstatic/sign.md
*/
export function getFile(path) {
return Axios.get(path, {
responseType: "text"
});
}

View File

@@ -1,5 +1,6 @@
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import { api as docApi } from "@/api/doc"; import { api as docApi } from "@/api/doc";
import { DocType } from "@/model/enum";
export const defaultActive = ref("overview.md"); export const defaultActive = ref("overview.md");
export const activeIndex = ref("1"); export const activeIndex = ref("1");
@@ -19,6 +20,8 @@ export const docDetail = ref({
url: "", url: "",
version: "", version: "",
docName: "", docName: "",
docTitle: "",
type: DocType.DOC,
description: "", description: "",
remark: "", remark: "",
requestParams: [], requestParams: [],
@@ -49,6 +52,14 @@ export const showDoc = computed(() => {
return docDetail.value?.docInfoView?.url?.length > 0; return docDetail.value?.docInfoView?.url?.length > 0;
}); });
export const isMarkdown = computed(() => {
return docDetail.value?.docInfoView?.type === DocType.MARKDOWN;
});
export const isDoc = computed(() => {
return docDetail.value?.docInfoView?.type === DocType.DOC;
});
/* /*
参数 类型 是否必填 最大长度 描述 示例值 参数 类型 是否必填 最大长度 描述 示例值
app_id String 是 32 平台分配给开发者的应用ID 2014072300007148 app_id String 是 32 平台分配给开发者的应用ID 2014072300007148
@@ -125,7 +136,7 @@ export const commonParams = ref([
maxLength: 3, maxLength: 3,
required: 1, required: 1,
description: "调用的接口版本固定为1.0", description: "调用的接口版本固定为1.0",
example: "xxxx" example: "1.0"
}, },
{ {
name: "app_auth_token", name: "app_auth_token",

View File

@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { import {
contentShow, isMarkdown,
isDoc,
content, content,
docDetail, docDetail,
commonParams, commonParams,
@@ -17,7 +18,8 @@ import {
showDoc showDoc
} from "./index"; } from "./index";
import { ApiParamTable } from "@/components/ApiParamTable"; import { ApiParamTable } from "@/components/ApiParamTable";
import MavonEditor from "mavon-editor"; import { useRouter } from "vue-router";
const router = useRouter();
const defaultProps = { const defaultProps = {
children: "children", children: "children",
label: "docTitle" label: "docTitle"
@@ -40,30 +42,21 @@ const defaultProps = {
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
<div v-show="docAppId > 0" style="margin-top: 10px">
<el-link type="primary" href="" target="_blank">签名算法</el-link>
</div>
<el-tree <el-tree
:data="docTree" :data="docTree"
:props="defaultProps" :props="defaultProps"
style="margin-top: 10px" style="margin-top: 10px"
highlight-current
@node-click="handleNodeClick" @node-click="handleNodeClick"
/> />
</el-aside> </el-aside>
<el-main v-show="showDoc" style="padding-top: 0"> <el-main v-show="showDoc" style="padding-top: 0">
<div v-show="contentShow"> <div v-show="isMarkdown">1</div>
<MavonEditor <div v-show="!isDoc">
v-model="content"
:box-shadow="false"
:subfield="false"
default-open="preview"
:editable="false"
:toolbars-flag="false"
/>
</div>
<div v-show="!contentShow">
<h2> <h2>
{{ docDetail.docInfoView.url + `(${docDetail.docInfoView.docName})` }} {{
docDetail.docInfoView.url + `(${docDetail.docInfoView.docTitle})`
}}
</h2> </h2>
<div v-if="docDetail.docInfoView.description"> <div v-if="docDetail.docInfoView.description">
<h3>接口说明</h3> <h3>接口说明</h3>
@@ -116,7 +109,13 @@ const defaultProps = {
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.name === 'sign'"> <span v-if="scope.row.name === 'sign'">
商户请求参数的签名串详见 商户请求参数的签名串详见
<el-button type="primary" link>签名</el-button> <router-link
:to="{ name: 'DocSign' }"
target="_blank"
class="sop-link"
>
签名算法
</router-link>
</span> </span>
<span v-else> <span v-else>
{{ scope.row.description }} {{ scope.row.description }}

View File

@@ -0,0 +1,3 @@
import { ref } from "vue";
export const value = ref("");

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import { value } from "./index";
import { getFile } from "@/utils/http";
import MarkdownEditor from "@/components/MarkdownEditor";
getFile("static/md/sign.md").then(resp => {
value.value = resp.data;
});
</script>
<template>
<MarkdownEditor :value="value" readonly />
</template>

View File

@@ -1,99 +0,0 @@
<script setup lang="ts">
import { hasAuth, getAuths } from "@/router/utils";
defineOptions({
name: "PermissionButtonRouter"
});
</script>
<template>
<div>
<p class="mb-2">当前拥有的code列表{{ getAuths() }}</p>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">组件方式判断权限</div>
</template>
<el-space wrap>
<Auth value="permission:btn:add">
<el-button plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
</Auth>
<Auth :value="['permission:btn:edit']">
<el-button plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
</Auth>
<Auth
:value="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
>
<el-button plain type="danger">
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</Auth>
</el-space>
</el-card>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">函数方式判断权限</div>
</template>
<el-space wrap>
<el-button v-if="hasAuth('permission:btn:add')" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button v-if="hasAuth(['permission:btn:edit'])" plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-if="
hasAuth([
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
])
"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
<el-card shadow="never">
<template #header>
<div class="card-header">
指令方式判断权限(该方式不能动态修改权限)
</div>
</template>
<el-space wrap>
<el-button v-auth="'permission:btn:add'" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button v-auth="['permission:btn:edit']" plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-auth="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
</div>
</template>

View File

@@ -1,109 +0,0 @@
<script setup lang="ts">
import { hasPerms } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user";
const { permissions } = useUserStoreHook();
defineOptions({
name: "PermissionButtonLogin"
});
</script>
<template>
<div>
<p class="mb-2">当前拥有的code列表{{ permissions }}</p>
<p v-show="permissions?.[0] === '*:*:*'" class="mb-2">
*:*:* 代表拥有全部按钮级别权限
</p>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">组件方式判断权限</div>
</template>
<el-space wrap>
<Perms value="permission:btn:add">
<el-button plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
</Perms>
<Perms :value="['permission:btn:edit']">
<el-button plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
</Perms>
<Perms
:value="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
>
<el-button plain type="danger">
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</Perms>
</el-space>
</el-card>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">函数方式判断权限</div>
</template>
<el-space wrap>
<el-button v-if="hasPerms('permission:btn:add')" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button
v-if="hasPerms(['permission:btn:edit'])"
plain
type="primary"
>
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-if="
hasPerms([
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
])
"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
<el-card shadow="never">
<template #header>
<div class="card-header">
指令方式判断权限(该方式不能动态修改权限)
</div>
</template>
<el-space wrap>
<el-button v-perms="'permission:btn:add'" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button v-perms="['permission:btn:edit']" plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-perms="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
</div>
</template>

View File

@@ -1,66 +0,0 @@
<script setup lang="ts">
import { initRouter } from "@/router/utils";
import { storageLocal } from "@pureadmin/utils";
import { type CSSProperties, ref, computed } from "vue";
import { useUserStoreHook } from "@/store/modules/user";
import { usePermissionStoreHook } from "@/store/modules/permission";
defineOptions({
name: "PermissionPage"
});
const elStyle = computed((): CSSProperties => {
return {
width: "85vw",
justifyContent: "start"
};
});
const username = ref(useUserStoreHook()?.username);
const options = [
{
value: "admin",
label: "管理员角色"
},
{
value: "common",
label: "普通角色"
}
];
function onChange() {
useUserStoreHook()
.loginByUsername({ username: username.value, password: "admin123" })
.then(res => {
if (res.success) {
storageLocal().removeItem("async-routes");
usePermissionStoreHook().clearAllCachePage();
initRouter();
}
});
}
</script>
<template>
<div>
<p class="mb-2">
模拟后台根据不同角色返回对应路由观察左侧菜单变化管理员角色可查看系统管理菜单普通角色不可查看系统管理菜单
</p>
<el-card shadow="never" :style="elStyle">
<template #header>
<div class="card-header">
<span>当前角色{{ username }}</span>
</div>
</template>
<el-select v-model="username" class="!w-[160px]" @change="onChange">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-card>
</div>
</template>