@jx3box/jx3box-editor 3.2.5 → 3.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.js +70 -0
- package/.storybook/middleware.js +73 -0
- package/.storybook/preview.js +90 -0
- package/package.json +9 -1
- package/src/ArticleMarkdown.vue +1 -1
- package/src/assets/css/markdown/markdown-article.less +13 -13
- package/src/assets/css/markdown/markdown-editor.less +11 -11
- package/src/components/Author.vue +34 -14
- package/src/components/QRcode.vue +1 -1
- package/src/service/cms.js +7 -1
- package/src/storybook/storybook-vars.less +1 -0
- package/src/storybook/storybook.helpers.js +154 -0
- package/stories/components/Author.stories.js +36 -0
- package/stories/components/Avatar.stories.js +55 -0
- package/stories/components/Combo.stories.js +37 -0
- package/stories/components/Letter.stories.js +30 -0
- package/stories/components/PostAuthor.stories.js +32 -0
- package/stories/components/QRcode.stories.js +37 -0
- package/stories/components/SkillMartial.stories.js +31 -0
- package/stories/exports/Article.stories.js +48 -0
- package/stories/exports/BoxResource.stories.js +31 -0
- package/stories/exports/Buff.stories.js +38 -0
- package/stories/exports/GameText.stories.js +38 -0
- package/stories/exports/Item.stories.js +51 -0
- package/stories/exports/ItemSimple.stories.js +50 -0
- package/stories/exports/Markdown.stories.js +46 -0
- package/stories/exports/Npc.stories.js +35 -0
- package/stories/exports/Resource.stories.js +29 -0
- package/stories/exports/Skill.stories.js +38 -0
- package/stories/exports/Tinymce.stories.js +46 -0
- package/stories/exports/Upload.stories.js +41 -0
- package/stories/exports/UploadAlbum.stories.js +31 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
const rootDir = path.resolve(__dirname, "..");
|
|
4
|
+
const srcDir = path.resolve(rootDir, "src");
|
|
5
|
+
const globalLess = path.resolve(srcDir, "assets/css/var.less");
|
|
6
|
+
const csslabBaseLess = path.resolve(rootDir, "node_modules/csslab/base.less");
|
|
7
|
+
const storybookVarsLess = path.resolve(srcDir, "storybook/storybook-vars.less");
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
framework: {
|
|
11
|
+
name: "@storybook/vue3-webpack5",
|
|
12
|
+
options: {},
|
|
13
|
+
},
|
|
14
|
+
stories: ["../stories/components/*.stories.js", "../stories/exports/*.stories.js"],
|
|
15
|
+
staticDirs: ["../public"],
|
|
16
|
+
addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions"],
|
|
17
|
+
docs: {
|
|
18
|
+
autodocs: "tag",
|
|
19
|
+
},
|
|
20
|
+
webpackFinal: async (config) => {
|
|
21
|
+
config.resolve = config.resolve || {};
|
|
22
|
+
config.resolve.alias = {
|
|
23
|
+
...(config.resolve.alias || {}),
|
|
24
|
+
"@": srcDir,
|
|
25
|
+
"@src": srcDir,
|
|
26
|
+
};
|
|
27
|
+
config.resolve.extensions = Array.from(new Set([...(config.resolve.extensions || []), ".vue", ".js", ".json"]));
|
|
28
|
+
|
|
29
|
+
config.module.rules.push(
|
|
30
|
+
{
|
|
31
|
+
test: /\.svg$/i,
|
|
32
|
+
resourceQuery: /inline/,
|
|
33
|
+
use: [
|
|
34
|
+
{
|
|
35
|
+
loader: require.resolve("vue-svg-inline-loader"),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
test: /\.less$/i,
|
|
41
|
+
use: [
|
|
42
|
+
require.resolve("style-loader"),
|
|
43
|
+
{
|
|
44
|
+
loader: require.resolve("css-loader"),
|
|
45
|
+
options: {
|
|
46
|
+
importLoaders: 2,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
require.resolve("postcss-loader"),
|
|
50
|
+
{
|
|
51
|
+
loader: require.resolve("less-loader"),
|
|
52
|
+
options: {
|
|
53
|
+
lessOptions: {
|
|
54
|
+
javascriptEnabled: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
loader: require.resolve("style-resources-loader"),
|
|
60
|
+
options: {
|
|
61
|
+
patterns: [csslabBaseLess, globalLess, storybookVarsLess],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return config;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
2
|
+
const JX3BOX = require("@jx3box/jx3box-common/data/jx3box.json");
|
|
3
|
+
|
|
4
|
+
const CMS_PROXY_TARGET = (process.env.VUE_APP_CMS || JX3BOX.__cms || "https://cms.jx3box.com").replace(/\/$/, "");
|
|
5
|
+
const { __cms, __node, __team, __next } = JX3BOX;
|
|
6
|
+
|
|
7
|
+
module.exports = function storybookMiddleware(router) {
|
|
8
|
+
router.use(
|
|
9
|
+
"/api/cms",
|
|
10
|
+
createProxyMiddleware({
|
|
11
|
+
target: CMS_PROXY_TARGET,
|
|
12
|
+
changeOrigin: true,
|
|
13
|
+
secure: true,
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
router.use(
|
|
18
|
+
"/api/node",
|
|
19
|
+
createProxyMiddleware({
|
|
20
|
+
target: __node,
|
|
21
|
+
changeOrigin: true,
|
|
22
|
+
secure: true,
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
router.use(
|
|
27
|
+
"/__proxy/cms",
|
|
28
|
+
createProxyMiddleware({
|
|
29
|
+
target: __cms,
|
|
30
|
+
changeOrigin: true,
|
|
31
|
+
secure: true,
|
|
32
|
+
pathRewrite: {
|
|
33
|
+
"^/__proxy/cms": "",
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
router.use(
|
|
39
|
+
"/__proxy/node",
|
|
40
|
+
createProxyMiddleware({
|
|
41
|
+
target: __node,
|
|
42
|
+
changeOrigin: true,
|
|
43
|
+
secure: true,
|
|
44
|
+
pathRewrite: {
|
|
45
|
+
"^/__proxy/node": "",
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
router.use(
|
|
51
|
+
"/__proxy/team",
|
|
52
|
+
createProxyMiddleware({
|
|
53
|
+
target: __team,
|
|
54
|
+
changeOrigin: true,
|
|
55
|
+
secure: true,
|
|
56
|
+
pathRewrite: {
|
|
57
|
+
"^/__proxy/team": "",
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
router.use(
|
|
63
|
+
"/__proxy/next",
|
|
64
|
+
createProxyMiddleware({
|
|
65
|
+
target: __next,
|
|
66
|
+
changeOrigin: true,
|
|
67
|
+
secure: true,
|
|
68
|
+
pathRewrite: {
|
|
69
|
+
"^/__proxy/next": "",
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { setup } from "@storybook/vue3";
|
|
2
|
+
import ElementPlus from "element-plus";
|
|
3
|
+
import "element-plus/dist/index.css";
|
|
4
|
+
import "@imengyu/vue3-context-menu/lib/vue3-context-menu.css";
|
|
5
|
+
import "../src/assets/css/var.less";
|
|
6
|
+
import "../src/assets/css/article.less";
|
|
7
|
+
import "../src/assets/css/markdown.less";
|
|
8
|
+
import "../src/assets/css/resource.less";
|
|
9
|
+
import "../src/assets/css/upload.less";
|
|
10
|
+
import "../src/assets/css/upload_album.less";
|
|
11
|
+
import "../src/assets/css/tinymce.less";
|
|
12
|
+
|
|
13
|
+
setup((app) => {
|
|
14
|
+
app.use(ElementPlus);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const ensureStorage = (key) => {
|
|
18
|
+
if (typeof window === "undefined" || window[key]) return;
|
|
19
|
+
const store = {};
|
|
20
|
+
window[key] = {
|
|
21
|
+
getItem(name) {
|
|
22
|
+
return Object.prototype.hasOwnProperty.call(store, name) ? store[name] : null;
|
|
23
|
+
},
|
|
24
|
+
setItem(name, value) {
|
|
25
|
+
store[name] = String(value);
|
|
26
|
+
},
|
|
27
|
+
removeItem(name) {
|
|
28
|
+
delete store[name];
|
|
29
|
+
},
|
|
30
|
+
clear() {
|
|
31
|
+
Object.keys(store).forEach((name) => delete store[name]);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
ensureStorage("localStorage");
|
|
37
|
+
ensureStorage("sessionStorage");
|
|
38
|
+
|
|
39
|
+
if (typeof window !== "undefined") {
|
|
40
|
+
window.__JX3BOX_STORYBOOK__ = true;
|
|
41
|
+
|
|
42
|
+
const originFetch = window.fetch?.bind(window);
|
|
43
|
+
if (originFetch) {
|
|
44
|
+
window.fetch = (input, init) => {
|
|
45
|
+
const nextInput = rewriteProxyRequest(input);
|
|
46
|
+
return originFetch(nextInput, init);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const parameters = {
|
|
52
|
+
controls: {
|
|
53
|
+
expanded: true,
|
|
54
|
+
sort: "requiredFirst",
|
|
55
|
+
},
|
|
56
|
+
actions: {
|
|
57
|
+
argTypesRegex: "^on[A-Z].*",
|
|
58
|
+
},
|
|
59
|
+
docs: {
|
|
60
|
+
toc: true,
|
|
61
|
+
},
|
|
62
|
+
layout: "centered",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function rewriteProxyRequest(input) {
|
|
66
|
+
if (typeof input === "string") {
|
|
67
|
+
return rewriteUrlString(input);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (input instanceof Request) {
|
|
71
|
+
return new Request(rewriteUrlString(input.url), input);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return input;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function rewriteUrlString(url) {
|
|
78
|
+
if (!url) return url;
|
|
79
|
+
|
|
80
|
+
if (url.startsWith("/__proxy/") || url.startsWith("/api/")) {
|
|
81
|
+
return url;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (url.startsWith("http://localhost:6006/__proxy/") || url.startsWith("http://127.0.0.1:6006/__proxy/")) {
|
|
85
|
+
const parsed = new URL(url);
|
|
86
|
+
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return url;
|
|
90
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jx3box/jx3box-editor",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.6",
|
|
4
4
|
"description": "JX3BOX Article & Editor",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"dev:tinymce": "serve -l 5120 ./tinymce",
|
|
10
10
|
"serve": "npm run dev",
|
|
11
11
|
"build": "npx lessc -x ./tinymce/skins/content/default/content.less ./tinymce/skins/content/default/content.min.css",
|
|
12
|
+
"storybook": "storybook dev -p 6006",
|
|
13
|
+
"build-storybook": "storybook build",
|
|
12
14
|
"lint": "vue-cli-service lint",
|
|
13
15
|
"update": "npm --registry https://registry.npmjs.org install @jx3box/jx3box-common@latest @jx3box/jx3box-data@latest @jx3box/jx3box-macro@latest @jx3box/jx3box-talent@latest @jx3box/jx3box-emotion@latest"
|
|
14
16
|
},
|
|
@@ -47,6 +49,10 @@
|
|
|
47
49
|
"devDependencies": {
|
|
48
50
|
"@babel/core": "^7.12.16",
|
|
49
51
|
"@babel/eslint-parser": "^7.12.16",
|
|
52
|
+
"@storybook/addon-essentials": "^8.6.14",
|
|
53
|
+
"@storybook/addon-interactions": "^8.6.14",
|
|
54
|
+
"@storybook/addon-links": "^8.6.14",
|
|
55
|
+
"@storybook/vue3-webpack5": "^8.6.14",
|
|
50
56
|
"@tailwindcss/postcss": "^4.2.1",
|
|
51
57
|
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
|
52
58
|
"@typescript-eslint/parser": "^5.31.0",
|
|
@@ -70,7 +76,9 @@
|
|
|
70
76
|
"prettier": "2.7.1",
|
|
71
77
|
"sass": "^1.97.3",
|
|
72
78
|
"sass-loader": "^16.0.7",
|
|
79
|
+
"qrcode.vue": "^3.6.0",
|
|
73
80
|
"serve": "^14.2.0",
|
|
81
|
+
"storybook": "^8.6.14",
|
|
74
82
|
"style-resources-loader": "^1.5.0",
|
|
75
83
|
"tailwindcss": "^4.2.1",
|
|
76
84
|
"typescript": "~4.5.5",
|
package/src/ArticleMarkdown.vue
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
// md文章样式
|
|
2
2
|
|
|
3
|
-
@import "
|
|
4
|
-
@import "
|
|
3
|
+
@import "./macro.less";
|
|
4
|
+
@import "./talent.less";
|
|
5
5
|
|
|
6
|
-
@import "
|
|
7
|
-
@import "
|
|
8
|
-
@import "
|
|
6
|
+
@import "./video.less";
|
|
7
|
+
@import "./macro.less";
|
|
8
|
+
@import "./talent.less";
|
|
9
9
|
|
|
10
|
-
@import 'module/directory.less';
|
|
11
|
-
@import 'module/icon.less';
|
|
12
|
-
@import 'module/resource.less';
|
|
13
|
-
@import 'module/jx3_element.less';
|
|
10
|
+
@import '../module/directory.less';
|
|
11
|
+
@import '../module/icon.less';
|
|
12
|
+
@import '../module/resource.less';
|
|
13
|
+
@import '../module/jx3_element.less';
|
|
14
14
|
|
|
15
|
-
@import 'module/buff.less';
|
|
16
|
-
@import 'module/skill.less';
|
|
17
|
-
@import 'module/item.less';
|
|
18
|
-
@import 'module/npc.less';
|
|
15
|
+
@import '../module/buff.less';
|
|
16
|
+
@import '../module/skill.less';
|
|
17
|
+
@import '../module/item.less';
|
|
18
|
+
@import '../module/npc.less';
|
|
@@ -62,16 +62,16 @@
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
@import "
|
|
66
|
-
@import "
|
|
67
|
-
@import "
|
|
65
|
+
@import "./video.less";
|
|
66
|
+
@import "./macro.less";
|
|
67
|
+
@import "./talent.less";
|
|
68
68
|
|
|
69
|
-
@import 'module/directory.less';
|
|
70
|
-
@import 'module/icon.less';
|
|
71
|
-
@import 'module/resource.less';
|
|
72
|
-
@import 'module/jx3_element.less';
|
|
69
|
+
@import '../module/directory.less';
|
|
70
|
+
@import '../module/icon.less';
|
|
71
|
+
@import '../module/resource.less';
|
|
72
|
+
@import '../module/jx3_element.less';
|
|
73
73
|
|
|
74
|
-
@import 'module/buff.less';
|
|
75
|
-
@import 'module/skill.less';
|
|
76
|
-
@import 'module/item.less';
|
|
77
|
-
@import 'module/npc.less';
|
|
74
|
+
@import '../module/buff.less';
|
|
75
|
+
@import '../module/skill.less';
|
|
76
|
+
@import '../module/item.less';
|
|
77
|
+
@import '../module/npc.less';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<!-- @圈人pop:作者卡片 -->
|
|
3
3
|
<div class="w-author" v-loading="loading">
|
|
4
|
-
<div class="w-author-wrapper el-popover" v-if="data" :style="
|
|
4
|
+
<div class="w-author-wrapper el-popover" :class="{ 'is-no-atcard': !bg }" v-if="data" :style="authorCardStyle">
|
|
5
5
|
<div class="u-author">
|
|
6
6
|
<Avatar
|
|
7
7
|
class="u-avatar"
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
<script>
|
|
72
72
|
import { authorLink, getLink, getMedalLink, getThumbnail } from "@jx3box/jx3box-common/js/utils";
|
|
73
73
|
import { getUserInfo, getUserMedals, getUserPublicTeams } from "../service/author.js";
|
|
74
|
-
import { getDecoration, getDecorationJson } from "../service/cms.js";
|
|
74
|
+
import { getDecoration, getDecorationJson, getDecorationV2 } from "../service/cms.js";
|
|
75
75
|
import User from "@jx3box/jx3box-common/js/user";
|
|
76
76
|
import JX3BOX from "@jx3box/jx3box-common/data/jx3box.json";
|
|
77
77
|
import Avatar from "./Avatar.vue";
|
|
78
|
-
const ATCARD_KEY = "
|
|
78
|
+
const ATCARD_KEY = "decoration_atcard_v2";
|
|
79
79
|
const DECORATION_JSON = "decoration_json";
|
|
80
80
|
const DECORATION_KEY = "decoration_me";
|
|
81
81
|
const HONOR_KEY = "honor_me";
|
|
@@ -123,6 +123,13 @@ export default {
|
|
|
123
123
|
isSuperAuthor: function () {
|
|
124
124
|
return !!this.data?.sign;
|
|
125
125
|
},
|
|
126
|
+
authorCardStyle: function () {
|
|
127
|
+
return this.bg
|
|
128
|
+
? {
|
|
129
|
+
backgroundImage: `url(${this.bg})`,
|
|
130
|
+
}
|
|
131
|
+
: {};
|
|
132
|
+
},
|
|
126
133
|
},
|
|
127
134
|
watch: {
|
|
128
135
|
uid: {
|
|
@@ -170,23 +177,30 @@ export default {
|
|
|
170
177
|
}
|
|
171
178
|
//已有缓存,读取解析
|
|
172
179
|
if (decoration_atcard) {
|
|
173
|
-
this.
|
|
180
|
+
this.setAtcardBackground(decoration_atcard);
|
|
174
181
|
return;
|
|
175
182
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
getDecorationV2({
|
|
184
|
+
using: 1,
|
|
185
|
+
user_id: this.uid,
|
|
186
|
+
type: "atcard",
|
|
187
|
+
subtype: "pc_atcard",
|
|
188
|
+
}).then((data) => {
|
|
189
|
+
let res = data?.data?.data || [];
|
|
190
|
+
let image = res[0]?.decorations?.[0]?.image;
|
|
191
|
+
if (!image) {
|
|
179
192
|
//空 则为无主题,不再加载接口,界面设No
|
|
180
193
|
sessionStorage.setItem(ATCARD_KEY + this.uid, "no");
|
|
181
194
|
this.bg = "";
|
|
182
195
|
return;
|
|
183
196
|
}
|
|
184
|
-
|
|
185
|
-
|
|
197
|
+
image = this.showDecorationImage(image);
|
|
198
|
+
sessionStorage.setItem(ATCARD_KEY + this.uid, image);
|
|
199
|
+
this.setAtcardBackground(image);
|
|
186
200
|
});
|
|
187
201
|
},
|
|
188
|
-
|
|
189
|
-
this.bg =
|
|
202
|
+
setAtcardBackground(val) {
|
|
203
|
+
this.bg = val || "";
|
|
190
204
|
},
|
|
191
205
|
getHonor() {
|
|
192
206
|
this.honor = "";
|
|
@@ -265,7 +279,7 @@ export default {
|
|
|
265
279
|
},
|
|
266
280
|
|
|
267
281
|
showMedalIcon: function (val) {
|
|
268
|
-
return __cdn + "/design/medals/user/" + val + ".
|
|
282
|
+
return __cdn + "/design/medals/user/" + val + ".webp";
|
|
269
283
|
},
|
|
270
284
|
medalLink: function ({ rank_id, medal_type = "rank" }) {
|
|
271
285
|
return getMedalLink(rank_id, medal_type);
|
|
@@ -282,8 +296,10 @@ export default {
|
|
|
282
296
|
showLevelColor: function (level) {
|
|
283
297
|
return __userLevelColor[level];
|
|
284
298
|
},
|
|
285
|
-
|
|
286
|
-
|
|
299
|
+
showDecorationImage: function (val) {
|
|
300
|
+
if (!val) return "";
|
|
301
|
+
if (/^(https?:)?\/\//.test(val) || /^(data|blob):/.test(val)) return val;
|
|
302
|
+
return __cdn.replace(/\/$/, "") + "/" + val.replace(/^\//, "");
|
|
287
303
|
},
|
|
288
304
|
authorLink,
|
|
289
305
|
},
|
|
@@ -297,6 +313,10 @@ export default {
|
|
|
297
313
|
background-repeat: no-repeat;
|
|
298
314
|
background-position: top right;
|
|
299
315
|
background-size: 100% auto;
|
|
316
|
+
&.is-no-atcard {
|
|
317
|
+
background-color: #f8fafc;
|
|
318
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
|
|
319
|
+
}
|
|
300
320
|
.u-author {
|
|
301
321
|
padding: 5px 0 15px 5px;
|
|
302
322
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<div class="u-qrcode">
|
|
12
12
|
<qrcode-vue class="u-pic" :value="value" :size="size" level="H"></qrcode-vue>
|
|
13
13
|
<span class="u-txt"
|
|
14
|
-
><img class="u-icon" svg-inline src="
|
|
14
|
+
><img class="u-icon" svg-inline src="../assets/img/other/qr-code.svg" />扫一扫手机访问</span
|
|
15
15
|
>
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
package/src/service/cms.js
CHANGED
|
@@ -28,6 +28,12 @@ function getDecoration(params) {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function getDecorationV2(params) {
|
|
32
|
+
return $cms().get(`/api/cms/user/decoration/v2`, {
|
|
33
|
+
params,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
function getDecorationJson() {
|
|
32
38
|
let url = __cdn + "design/decoration/index.json";
|
|
33
39
|
return axios.get(url);
|
|
@@ -39,4 +45,4 @@ function getLetterPaper(params) {
|
|
|
39
45
|
params,
|
|
40
46
|
});
|
|
41
47
|
}
|
|
42
|
-
export { uploadFile, loadAuthors, loadEmotions, getDecoration, getDecorationJson, getLetterPaper };
|
|
48
|
+
export { uploadFile, loadAuthors, loadEmotions, getDecoration, getDecorationV2, getDecorationJson, getLetterPaper };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@bg-black: #111827;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { onMounted, ref } from "vue";
|
|
2
|
+
|
|
3
|
+
export function createMeta({ title, component, args = {}, argTypes = {}, docs = "", parameters = {} }) {
|
|
4
|
+
return {
|
|
5
|
+
title,
|
|
6
|
+
component,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
args,
|
|
9
|
+
argTypes: buildArgTypes(argTypes),
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: "padded",
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: docs,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
...parameters,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function componentStory(component, options = {}) {
|
|
23
|
+
const { template, style = "", components = {}, setup } = options;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
render: (args) => ({
|
|
27
|
+
components: {
|
|
28
|
+
StoryComponent: component,
|
|
29
|
+
...components,
|
|
30
|
+
},
|
|
31
|
+
setup() {
|
|
32
|
+
return {
|
|
33
|
+
args,
|
|
34
|
+
...(typeof setup === "function" ? setup(args) : {}),
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
template:
|
|
38
|
+
template ||
|
|
39
|
+
`<div style="${style}"><StoryComponent v-bind="args" /></div>`,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function previewStory(component, options = {}) {
|
|
45
|
+
const { title = "效果预览", description = "", code = "", style = "", template, components = {}, setup } = options;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
render: (args) => ({
|
|
49
|
+
components: {
|
|
50
|
+
StoryComponent: component,
|
|
51
|
+
...components,
|
|
52
|
+
},
|
|
53
|
+
setup() {
|
|
54
|
+
return {
|
|
55
|
+
args,
|
|
56
|
+
previewTitle: title,
|
|
57
|
+
previewDescription: description,
|
|
58
|
+
previewCode: code,
|
|
59
|
+
...(typeof setup === "function" ? setup(args) : {}),
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
template: `
|
|
63
|
+
<div style="display:grid;gap:16px;min-width:320px;">
|
|
64
|
+
<div style="padding:16px 18px;border:1px solid #e5e7eb;border-radius:12px;background:#fff;">
|
|
65
|
+
<div style="font-size:16px;font-weight:600;color:#111827;">{{ previewTitle }}</div>
|
|
66
|
+
<div v-if="previewDescription" style="margin-top:8px;color:#4b5563;line-height:1.7;white-space:pre-line;">{{ previewDescription }}</div>
|
|
67
|
+
<pre v-if="previewCode" style="margin:12px 0 0;padding:12px;border-radius:10px;background:#0f172a;color:#e2e8f0;font-size:12px;line-height:1.6;overflow:auto;white-space:pre-wrap;word-break:break-word;">{{ previewCode }}</pre>
|
|
68
|
+
</div>
|
|
69
|
+
<div style="padding:20px;border:1px solid #e5e7eb;border-radius:12px;background:#fff;${style}">
|
|
70
|
+
${template || `<StoryComponent v-bind="args" />`}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
`,
|
|
74
|
+
}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function docsOnlyStory(message) {
|
|
79
|
+
return {
|
|
80
|
+
render: () => ({
|
|
81
|
+
template: `<div style="max-width:720px;padding:16px 18px;border:1px solid #e5e7eb;border-radius:12px;background:#fff;color:#374151;line-height:1.7;">${escapeHtml(
|
|
82
|
+
message
|
|
83
|
+
)}</div>`,
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function useDemoText(url, fallback = "") {
|
|
89
|
+
const content = ref(fallback);
|
|
90
|
+
const loaded = ref(false);
|
|
91
|
+
|
|
92
|
+
onMounted(async () => {
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch(url);
|
|
95
|
+
if (response.ok) {
|
|
96
|
+
content.value = await response.text();
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.warn(`[storybook] failed to load demo content: ${url}`, error);
|
|
100
|
+
} finally {
|
|
101
|
+
loaded.value = true;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
content,
|
|
107
|
+
loaded,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildArgTypes(argTypes) {
|
|
112
|
+
return Object.fromEntries(
|
|
113
|
+
Object.entries(argTypes).map(([name, config]) => {
|
|
114
|
+
const table = {};
|
|
115
|
+
|
|
116
|
+
if (config.type) {
|
|
117
|
+
table.type = { summary: config.type };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (config.defaultValue !== undefined) {
|
|
121
|
+
table.defaultValue = { summary: config.defaultValue };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (config.required) {
|
|
125
|
+
table.category = "required";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return [
|
|
129
|
+
name,
|
|
130
|
+
{
|
|
131
|
+
description: config.description,
|
|
132
|
+
control: config.control === false ? false : config.control || inferControl(config.type),
|
|
133
|
+
options: config.options,
|
|
134
|
+
table,
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function inferControl(type = "") {
|
|
142
|
+
if (type.includes("Boolean")) return "boolean";
|
|
143
|
+
if (type.includes("Number")) return "number";
|
|
144
|
+
if (type.includes("Array") || type.includes("Object")) return "object";
|
|
145
|
+
return "text";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function escapeHtml(value) {
|
|
149
|
+
return String(value)
|
|
150
|
+
.replace(/&/g, "&")
|
|
151
|
+
.replace(/</g, "<")
|
|
152
|
+
.replace(/>/g, ">")
|
|
153
|
+
.replace(/\n/g, "<br />");
|
|
154
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Author from "../../src/components/Author.vue";
|
|
2
|
+
import { previewStory } from "../../src/storybook/storybook.helpers";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "Components/Author",
|
|
6
|
+
component: Author,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
args: {
|
|
9
|
+
uid: 10086,
|
|
10
|
+
},
|
|
11
|
+
argTypes: {
|
|
12
|
+
uid: {
|
|
13
|
+
description: "作者 UID。",
|
|
14
|
+
control: "text",
|
|
15
|
+
table: { type: { summary: "String | Number" }, category: "required" },
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
parameters: {
|
|
19
|
+
layout: "padded",
|
|
20
|
+
docs: {
|
|
21
|
+
description: {
|
|
22
|
+
component: "作者信息卡片。组件会在侦听 `uid` 后请求远程作者数据,因此在 Storybook 中主要用于查看 props 文档。",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const Overview = previewStory(Author, {
|
|
29
|
+
title: "作者卡片预览",
|
|
30
|
+
description: "这里直接使用项目 demo 中出现过的作者 UID:`8`。如果接口可用,就能看到完整作者卡片,而不是只有 props 表。",
|
|
31
|
+
code: `<Author :uid="8" />`,
|
|
32
|
+
style: "min-width: 420px; display: flex; justify-content: flex-start;",
|
|
33
|
+
});
|
|
34
|
+
Overview.args = {
|
|
35
|
+
uid: 8,
|
|
36
|
+
};
|