@jx3box/jx3box-ui 2.0.4 → 2.0.5
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 +208 -0
- package/.storybook/preview.js +84 -0
- package/README.md +38 -78
- package/package.json +9 -2
- package/src/CommonFooter.vue +2 -2
- package/src/footer/resource.vue +35 -2
- package/src/header/box2.vue +9 -4
- package/src/stories/layout/CommonFooter.stories.js +22 -0
- package/src/stories/layout/CommonHeader.stories.js +39 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
const lessResourcePatterns = [
|
|
4
|
+
path.resolve(__dirname, '../node_modules/@jx3box/jx3box-common/css/var.less'),
|
|
5
|
+
path.resolve(__dirname, '../node_modules/@jx3box/jx3box-common/css/mixin.less'),
|
|
6
|
+
path.resolve(__dirname, '../assets/css/var.less'),
|
|
7
|
+
path.resolve(__dirname, '../assets/css/mixin.less'),
|
|
8
|
+
path.resolve(__dirname, '../node_modules/csslab/base.less'),
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
function walkRules(rules, visitor) {
|
|
12
|
+
if (!Array.isArray(rules)) return;
|
|
13
|
+
rules.forEach((rule) => {
|
|
14
|
+
visitor(rule);
|
|
15
|
+
if (Array.isArray(rule.oneOf)) walkRules(rule.oneOf, visitor);
|
|
16
|
+
if (Array.isArray(rule.rules)) walkRules(rule.rules, visitor);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ensureVueSvgInlineLoader(config) {
|
|
21
|
+
walkRules(config.module?.rules, (rule) => {
|
|
22
|
+
if (!Array.isArray(rule.use)) return;
|
|
23
|
+
|
|
24
|
+
const hasVueLoader = rule.use.some((use) => {
|
|
25
|
+
const loader = typeof use === 'string' ? use : use?.loader;
|
|
26
|
+
return loader && loader.includes('vue-loader');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!hasVueLoader) return;
|
|
30
|
+
|
|
31
|
+
const hasSvgInlineLoader = rule.use.some((use) => {
|
|
32
|
+
const loader = typeof use === 'string' ? use : use?.loader;
|
|
33
|
+
return loader && loader.includes('vue-svg-inline-loader');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!hasSvgInlineLoader) {
|
|
37
|
+
rule.use.push({
|
|
38
|
+
loader: require.resolve('vue-svg-inline-loader'),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function ensureLessGlobalResources(config) {
|
|
45
|
+
walkRules(config.module?.rules, (rule) => {
|
|
46
|
+
if (!Array.isArray(rule.use)) return;
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < rule.use.length; i += 1) {
|
|
49
|
+
const currentUse = rule.use[i];
|
|
50
|
+
const loader = typeof currentUse === 'string' ? currentUse : currentUse?.loader;
|
|
51
|
+
|
|
52
|
+
if (!loader || !loader.includes('less-loader')) continue;
|
|
53
|
+
|
|
54
|
+
const hasStyleResource = rule.use.some((use) => {
|
|
55
|
+
const useLoader = typeof use === 'string' ? use : use?.loader;
|
|
56
|
+
return useLoader && useLoader.includes('style-resources-loader');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!hasStyleResource) {
|
|
60
|
+
rule.use.splice(i + 1, 0, {
|
|
61
|
+
loader: require.resolve('style-resources-loader'),
|
|
62
|
+
options: {
|
|
63
|
+
patterns: lessResourcePatterns,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function removeVueDocgenLoader(config) {
|
|
74
|
+
walkRules(config.module?.rules, (rule) => {
|
|
75
|
+
const directLoader = typeof rule.loader === 'string' ? rule.loader : '';
|
|
76
|
+
if (directLoader.includes('vue-docgen-loader')) {
|
|
77
|
+
rule.__remove = true;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!Array.isArray(rule.use)) return;
|
|
82
|
+
rule.use = rule.use.filter((use) => {
|
|
83
|
+
const loader = typeof use === 'string' ? use : use?.loader;
|
|
84
|
+
return !(loader && loader.includes('vue-docgen-loader'));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (Array.isArray(config.module?.rules)) {
|
|
89
|
+
config.module.rules = config.module.rules.filter((rule) => !rule.__remove);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function ensureStyleRules(config) {
|
|
94
|
+
const rules = config.module?.rules || [];
|
|
95
|
+
const hasLessRule = rules.some((rule) => String(rule.test).includes('less'));
|
|
96
|
+
const hasScssRule = rules.some((rule) => String(rule.test).includes('scss'));
|
|
97
|
+
const hasCssRule = rules.some((rule) => String(rule.test).includes('\\.css$'));
|
|
98
|
+
|
|
99
|
+
if (hasCssRule) {
|
|
100
|
+
walkRules(rules, (rule) => {
|
|
101
|
+
if (!String(rule.test).includes('\\.css$') || !Array.isArray(rule.use)) return;
|
|
102
|
+
const hasPostcss = rule.use.some((use) => {
|
|
103
|
+
const loader = typeof use === 'string' ? use : use?.loader;
|
|
104
|
+
return loader && loader.includes('postcss-loader');
|
|
105
|
+
});
|
|
106
|
+
if (!hasPostcss) {
|
|
107
|
+
rule.use.push({
|
|
108
|
+
loader: require.resolve('postcss-loader'),
|
|
109
|
+
options: {
|
|
110
|
+
postcssOptions: {
|
|
111
|
+
config: path.resolve(__dirname, '../postcss.config.js'),
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!hasLessRule) {
|
|
120
|
+
rules.push({
|
|
121
|
+
test: /\.less$/i,
|
|
122
|
+
use: [
|
|
123
|
+
require.resolve('style-loader'),
|
|
124
|
+
{
|
|
125
|
+
loader: require.resolve('css-loader'),
|
|
126
|
+
options: { importLoaders: 1 },
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
loader: require.resolve('postcss-loader'),
|
|
130
|
+
options: {
|
|
131
|
+
postcssOptions: {
|
|
132
|
+
config: path.resolve(__dirname, '../postcss.config.js'),
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
loader: require.resolve('less-loader'),
|
|
138
|
+
options: {
|
|
139
|
+
lessOptions: {
|
|
140
|
+
javascriptEnabled: true,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
loader: require.resolve('style-resources-loader'),
|
|
146
|
+
options: {
|
|
147
|
+
patterns: lessResourcePatterns,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!hasScssRule) {
|
|
155
|
+
rules.push({
|
|
156
|
+
test: /\.s[ac]ss$/i,
|
|
157
|
+
use: [
|
|
158
|
+
require.resolve('style-loader'),
|
|
159
|
+
require.resolve('css-loader'),
|
|
160
|
+
{
|
|
161
|
+
loader: require.resolve('postcss-loader'),
|
|
162
|
+
options: {
|
|
163
|
+
postcssOptions: {
|
|
164
|
+
config: path.resolve(__dirname, '../postcss.config.js'),
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
require.resolve('sass-loader'),
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
config.module.rules = rules;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** @type { import('@storybook/vue3-webpack5').StorybookConfig } */
|
|
177
|
+
module.exports = {
|
|
178
|
+
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
|
179
|
+
addons: [
|
|
180
|
+
{
|
|
181
|
+
name: '@storybook/addon-essentials',
|
|
182
|
+
options: {
|
|
183
|
+
docs: false,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
'@storybook/addon-interactions',
|
|
187
|
+
],
|
|
188
|
+
framework: {
|
|
189
|
+
name: '@storybook/vue3-webpack5',
|
|
190
|
+
options: {
|
|
191
|
+
docgen: false,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
webpackFinal: async (config) => {
|
|
195
|
+
config.resolve = config.resolve || {};
|
|
196
|
+
config.resolve.alias = {
|
|
197
|
+
...(config.resolve.alias || {}),
|
|
198
|
+
'@': path.resolve(__dirname, '../src'),
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
removeVueDocgenLoader(config);
|
|
202
|
+
ensureStyleRules(config);
|
|
203
|
+
ensureVueSvgInlineLoader(config);
|
|
204
|
+
ensureLessGlobalResources(config);
|
|
205
|
+
|
|
206
|
+
return config;
|
|
207
|
+
},
|
|
208
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { setup } from '@storybook/vue3';
|
|
2
|
+
import ElementPlus from 'element-plus';
|
|
3
|
+
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
|
4
|
+
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
|
|
7
|
+
import { Jx3boxUiI18n } from '../i18n';
|
|
8
|
+
import navData from '../assets/data/nav.json';
|
|
9
|
+
import boxData from '../assets/data/box.json';
|
|
10
|
+
|
|
11
|
+
import '@jx3box/jx3box-common/css/normalize.css';
|
|
12
|
+
import '@jx3box/jx3box-common/css/font.css';
|
|
13
|
+
import 'element-plus/dist/index.css';
|
|
14
|
+
import '@jx3box/jx3box-common/css/tailwind.css';
|
|
15
|
+
|
|
16
|
+
let axiosPatched = false;
|
|
17
|
+
|
|
18
|
+
function patchAxiosForStorybook() {
|
|
19
|
+
if (axiosPatched) return;
|
|
20
|
+
|
|
21
|
+
const originalGet = axios.get.bind(axios);
|
|
22
|
+
axios.get = (url, ...args) => {
|
|
23
|
+
if (typeof url === 'string' && url.includes('/config/global.json')) {
|
|
24
|
+
return Promise.resolve({
|
|
25
|
+
data: {
|
|
26
|
+
token_version: 'storybook',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return originalGet(url, ...args);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
axiosPatched = true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function bootstrapRuntimeData() {
|
|
37
|
+
try {
|
|
38
|
+
sessionStorage.setItem('nav', JSON.stringify(navData));
|
|
39
|
+
sessionStorage.setItem('box', JSON.stringify(boxData));
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// ignore session storage failures in restricted contexts
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
localStorage.removeItem('token');
|
|
46
|
+
localStorage.removeItem('uid');
|
|
47
|
+
localStorage.removeItem('group');
|
|
48
|
+
localStorage.removeItem('created_at');
|
|
49
|
+
localStorage.removeItem('name');
|
|
50
|
+
localStorage.removeItem('status');
|
|
51
|
+
localStorage.removeItem('avatar');
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// ignore local storage failures in restricted contexts
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setup((app) => {
|
|
58
|
+
app.use(Jx3boxUiI18n, { locale: 'zh-CN' });
|
|
59
|
+
app.use(ElementPlus, { locale: zhCn });
|
|
60
|
+
|
|
61
|
+
for (const [name, component] of Object.entries(ElementPlusIconsVue)) {
|
|
62
|
+
app.component(name, component);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
patchAxiosForStorybook();
|
|
67
|
+
|
|
68
|
+
export const decorators = [
|
|
69
|
+
(story) => {
|
|
70
|
+
bootstrapRuntimeData();
|
|
71
|
+
return story();
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
export const parameters = {
|
|
76
|
+
actions: { argTypesRegex: '^on[A-Z].*' },
|
|
77
|
+
controls: {
|
|
78
|
+
matchers: {
|
|
79
|
+
color: /(background|color)$/i,
|
|
80
|
+
date: /Date$/i,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
layout: 'fullscreen',
|
|
84
|
+
};
|
package/README.md
CHANGED
|
@@ -5,40 +5,32 @@
|
|
|
5
5
|
|
|
6
6
|
## 模块
|
|
7
7
|
|
|
8
|
-
###
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
### 全局
|
|
9
|
+
- CommonHeader 公共头
|
|
10
|
+
- Breadcrumb 面包屑
|
|
11
|
+
- Main 默认内容框架(非必须)
|
|
12
|
+
- LeftSidebar 左侧边栏(非必须)
|
|
13
|
+
- LeftSideToggle 左侧边栏触发器(非必须,可独立使用)
|
|
14
|
+
- RightSidebar 右侧边栏(非必须)
|
|
15
|
+
- CommonFooter 公共底
|
|
16
|
+
- SuspendCommon 移动侧浮窗底(此组件需要手动引入)
|
|
17
|
+
|
|
18
|
+
### 内容单页 single
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
- AuthorFollow 关注用户(区分rss,此处用户更新不会收到消息)
|
|
14
|
-
- AuthorGift 打赏用户
|
|
15
|
-
- AuthorHonor 用户称号
|
|
16
|
-
- AuthorInfo 用户基础信息
|
|
17
|
-
- AuthorLink 用户绑定平台账号(微博,github,直播间)
|
|
18
|
-
- AuthorMedals 用户勋章
|
|
19
|
-
- AuthorPosts 用户作品
|
|
20
|
-
- AuthorRss 订阅用户
|
|
21
|
-
- AuthorTeams 所在团队
|
|
22
|
-
- Avatar 头像
|
|
23
|
-
- UserPop 搜索用户弹窗
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
### 管理员模块 bread
|
|
27
|
-
|
|
28
|
-
一般用于文章详情的右上角管理按钮
|
|
29
|
-
|
|
30
|
-
- AdminDrop 管理模块触发器
|
|
31
|
-
- Admin 文章管理模块(管理员)
|
|
32
|
-
- AdminDirectMessage 管理员私信
|
|
33
|
-
- Crumb 栏目动态
|
|
34
|
-
- DesignTask 设计任务
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### 评论模块 comment
|
|
20
|
+
主要是用于详情页的展示内容
|
|
38
21
|
|
|
39
|
-
|
|
22
|
+
- CmsSingle 单页框架
|
|
23
|
+
- Author 侧边栏作者信息整合
|
|
24
|
+
- PostDirectory 目录
|
|
25
|
+
- PostHeader 创作信息
|
|
26
|
+
- Creators 联合创作
|
|
27
|
+
- Collection 小册
|
|
28
|
+
- Article 文章内容
|
|
29
|
+
- Thx 交互组件
|
|
30
|
+
- SimpleThx 交互组件简化版(仅PVP栏目使用,需要手动引入)
|
|
31
|
+
- Comment 评论
|
|
40
32
|
|
|
41
|
-
### 筛选模块
|
|
33
|
+
### 筛选模块 list
|
|
42
34
|
|
|
43
35
|
主要用于各栏目的列表筛选
|
|
44
36
|
|
|
@@ -46,58 +38,26 @@
|
|
|
46
38
|
- markBy 标记筛选
|
|
47
39
|
- menuBy 通用筛选
|
|
48
40
|
- orderBy 排序筛选
|
|
49
|
-
- tagBy
|
|
41
|
+
- tagBy 标签筛选
|
|
50
42
|
- topicBy 主题筛选
|
|
51
43
|
- versionBy 版本筛选
|
|
52
44
|
- zlpBy 资料片筛选
|
|
53
45
|
|
|
46
|
+
### 个人模块 author
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
主要是header的模块
|
|
58
|
-
|
|
59
|
-
- alternate 马甲切换
|
|
60
|
-
- asset 用户资产
|
|
61
|
-
- box 魔盒矩阵(移动端)
|
|
62
|
-
- box2 魔盒矩阵(PC)
|
|
63
|
-
- client 客户端切换
|
|
64
|
-
- lang 语言切换
|
|
65
|
-
- logo
|
|
66
|
-
- manage 后台管理入口
|
|
67
|
-
- message 消息中心入口
|
|
68
|
-
- nav 导航栏
|
|
69
|
-
- publish 创作中心入口
|
|
70
|
-
- search 搜索框
|
|
71
|
-
- shop 商城入口
|
|
72
|
-
- user 用户头像+信息pop
|
|
73
|
-
- 会员中心入口
|
|
74
|
-
|
|
75
|
-
### 交互模块
|
|
76
|
-
|
|
77
|
-
主要是文章详情中用于用户交互的组件,包含打赏,分享,收藏等等
|
|
78
|
-
|
|
79
|
-
- BoxcoinAdmin 管理员品鉴
|
|
80
|
-
- BoxcoinRecords 品鉴+打赏记录
|
|
81
|
-
- BoxcoinUser 用户打赏
|
|
82
|
-
- Contributors 多人合作文章中打赏目标选择
|
|
83
|
-
- Fav 收藏
|
|
84
|
-
- Like 点赞
|
|
85
|
-
- Rss 订阅
|
|
86
|
-
- Share 分享
|
|
87
|
-
- WatchLater 稍后再看
|
|
88
|
-
|
|
48
|
+
一般用于文章详情的左侧边栏,个人主页等
|
|
89
49
|
|
|
90
|
-
|
|
50
|
+
- Avatar 头像
|
|
51
|
+
- AuthorFans 粉丝榜
|
|
52
|
+
- AuthorFollow 关注用户(区分rss,此处用户更新不会收到消息)
|
|
53
|
+
- AuthorGift 打赏用户
|
|
54
|
+
- AuthorHonor 用户称号
|
|
55
|
+
- AuthorInfo 用户基础信息
|
|
56
|
+
- AuthorLink 用户绑定平台账号(微博,github,直播间)
|
|
57
|
+
- AuthorMedals 用户勋章
|
|
58
|
+
- AuthorPosts 用户作品
|
|
59
|
+
- AuthorRss 订阅用户
|
|
60
|
+
- AuthorTeams 所在团队
|
|
61
|
+
- UserPop 搜索用户弹窗
|
|
91
62
|
|
|
92
|
-
主要是用于详情页的展示内容
|
|
93
63
|
|
|
94
|
-
- Author 侧边栏作者信息
|
|
95
|
-
- CmsSingle 单页框架
|
|
96
|
-
- Collection 小册
|
|
97
|
-
- Comment 评论
|
|
98
|
-
- Creators 联合创作
|
|
99
|
-
- PostDirectory 目录
|
|
100
|
-
- PostHeader 创作信息
|
|
101
|
-
- PostTopic 右侧边栏头条
|
|
102
|
-
- SimpleThx 简单交互组件集合
|
|
103
|
-
- Thx 交互组件
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jx3box/jx3box-ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "JX3BOX Vue3 UI",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vue-cli-service serve --mode development",
|
|
8
8
|
"serve": "vue-cli-service serve --mode production",
|
|
9
9
|
"debug": "vue-cli-service serve --mode debug",
|
|
10
|
+
"storybook": "storybook dev -p 6006",
|
|
11
|
+
"build-storybook": "storybook build",
|
|
10
12
|
"build": "npm run build:prod",
|
|
11
13
|
"build:dev": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vue-cli-service build --mode development",
|
|
12
14
|
"build:prod": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vue-cli-service build --mode production",
|
|
@@ -31,7 +33,7 @@
|
|
|
31
33
|
},
|
|
32
34
|
"dependencies": {
|
|
33
35
|
"@element-plus/icons-vue": "^2.3.2",
|
|
34
|
-
"@jx3box/jx3box-common": "^9.1.
|
|
36
|
+
"@jx3box/jx3box-common": "^9.1.1",
|
|
35
37
|
"@jx3box/jx3box-data": "^3.9.2",
|
|
36
38
|
"@jx3box/jx3box-emotion": "^1.3.0",
|
|
37
39
|
"@jx3box/jx3box-macro": "^1.0.3",
|
|
@@ -60,6 +62,10 @@
|
|
|
60
62
|
"devDependencies": {
|
|
61
63
|
"@babel/core": "^7.12.16",
|
|
62
64
|
"@babel/eslint-parser": "^7.28.6",
|
|
65
|
+
"@storybook/addon-essentials": "^8.6.18",
|
|
66
|
+
"@storybook/addon-interactions": "^8.6.18",
|
|
67
|
+
"@storybook/test": "^8.6.18",
|
|
68
|
+
"@storybook/vue3-webpack5": "^8.6.18",
|
|
63
69
|
"@tailwindcss/postcss": "^4.2.1",
|
|
64
70
|
"@vue/cli-plugin-babel": "~5.0.0",
|
|
65
71
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
|
@@ -77,6 +83,7 @@
|
|
|
77
83
|
"prettier": "2.7.1",
|
|
78
84
|
"sass": "^1.97.3",
|
|
79
85
|
"sass-loader": "^16.0.7",
|
|
86
|
+
"storybook": "^8.6.18",
|
|
80
87
|
"style-resources-loader": "^1.5.0",
|
|
81
88
|
"tailwindcss": "^4.2.1",
|
|
82
89
|
"vue-svg-inline-loader": "^2.1.3"
|
package/src/CommonFooter.vue
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
<div class="mx-auto w-full" style="max-width: 92rem">
|
|
4
4
|
<div class="grid grid-cols-1 gap-10 pb-10 lg:grid-cols-12 lg:gap-8">
|
|
5
5
|
<section class="lg:col-span-3">
|
|
6
|
-
<
|
|
6
|
+
<a class="flex items-center space-x-3 cursor-pointer" href="/">
|
|
7
7
|
<img class="u-logo h-9 w-9" svg-inline src="../assets/img/common/logo.svg" alt="JX3BOX" />
|
|
8
8
|
<span class="text-2xl font-bold tracking-tight text-white">JX3BOX</span>
|
|
9
|
-
</
|
|
9
|
+
</a>
|
|
10
10
|
<p class="mt-5 text-sm leading-7 text-gray-400" style="max-width: 20rem">
|
|
11
11
|
{{ $jx3boxT("jx3boxUi.footer.slogan1", "一站式剑三工具与资源聚合站。") }}<br />
|
|
12
12
|
{{ $jx3boxT("jx3boxUi.footer.slogan2", "江湖路远,幸甚有你。") }}
|
package/src/footer/resource.vue
CHANGED
|
@@ -17,9 +17,35 @@
|
|
|
17
17
|
<template #reference>
|
|
18
18
|
<a
|
|
19
19
|
class="flex h-12 items-center rounded-xl border border-gray-700 bg-gray-800 px-4 text-xs text-gray-300 transition hover:border-blue-500 hover:bg-gray-700"
|
|
20
|
-
:href="item.href || '
|
|
20
|
+
:href="item.href || '#'"
|
|
21
21
|
:target="item.href ? '_blank' : null"
|
|
22
22
|
:rel="item.href ? 'noopener noreferrer' : null"
|
|
23
|
+
@click="handleLinkClick($event, item)"
|
|
24
|
+
>
|
|
25
|
+
<span class="mr-2.5 flex h-4 w-4 items-center justify-center">
|
|
26
|
+
<img class="h-4 w-4" :src="item.icon" :alt="getDownloadName(item)" />
|
|
27
|
+
</span>
|
|
28
|
+
<span>{{ getDownloadName(item) }}</span>
|
|
29
|
+
</a>
|
|
30
|
+
</template>
|
|
31
|
+
</el-popover>
|
|
32
|
+
<el-popover
|
|
33
|
+
v-else-if="item.placeholder"
|
|
34
|
+
trigger="hover"
|
|
35
|
+
placement="top"
|
|
36
|
+
:show-after="150"
|
|
37
|
+
popper-class="c-footer--v4__popover"
|
|
38
|
+
>
|
|
39
|
+
<div class="p-3 text-center text-xs font-semibold">
|
|
40
|
+
{{ item.placeholder }}
|
|
41
|
+
</div>
|
|
42
|
+
<template #reference>
|
|
43
|
+
<a
|
|
44
|
+
class="flex h-12 items-center rounded-xl border border-gray-700 bg-gray-800 px-4 text-xs text-gray-300 transition hover:border-blue-500 hover:bg-gray-700"
|
|
45
|
+
:href="item.href || '#'"
|
|
46
|
+
:target="item.href ? '_blank' : null"
|
|
47
|
+
:rel="item.href ? 'noopener noreferrer' : null"
|
|
48
|
+
@click="handleLinkClick($event, item)"
|
|
23
49
|
>
|
|
24
50
|
<span class="mr-2.5 flex h-4 w-4 items-center justify-center">
|
|
25
51
|
<img class="h-4 w-4" :src="item.icon" :alt="getDownloadName(item)" />
|
|
@@ -31,9 +57,10 @@
|
|
|
31
57
|
<a
|
|
32
58
|
v-else
|
|
33
59
|
class="flex h-12 items-center rounded-xl border border-gray-700 bg-gray-800 px-4 text-xs text-gray-300 transition hover:border-blue-500 hover:bg-gray-700"
|
|
34
|
-
:href="item.href || '
|
|
60
|
+
:href="item.href || '#'"
|
|
35
61
|
:target="item.href ? '_blank' : null"
|
|
36
62
|
:rel="item.href ? 'noopener noreferrer' : null"
|
|
63
|
+
@click="handleLinkClick($event, item)"
|
|
37
64
|
>
|
|
38
65
|
<span class="mr-2.5 flex h-4 w-4 items-center justify-center">
|
|
39
66
|
<img class="h-4 w-4" :src="item.icon" :alt="getDownloadName(item)" />
|
|
@@ -98,6 +125,7 @@ export default {
|
|
|
98
125
|
href: "",
|
|
99
126
|
icon: require("../../assets/img/common/ios.svg"),
|
|
100
127
|
// qrcode: require("../../assets/img/common/ios.jpg"),
|
|
128
|
+
placeholder: "即将上线",
|
|
101
129
|
},
|
|
102
130
|
{
|
|
103
131
|
key: "android",
|
|
@@ -105,6 +133,7 @@ export default {
|
|
|
105
133
|
href: "",
|
|
106
134
|
icon: require("../../assets/img/common/android.svg"),
|
|
107
135
|
// qrcode: require("../../assets/img/common/android.jpg"),
|
|
136
|
+
placeholder: "即将上线",
|
|
108
137
|
},
|
|
109
138
|
{
|
|
110
139
|
key: "harmonyNext",
|
|
@@ -112,6 +141,7 @@ export default {
|
|
|
112
141
|
href: "",
|
|
113
142
|
icon: require("../../assets/img/common/harmony.svg"),
|
|
114
143
|
// qrcode: require("../../assets/img/common/harmony.jpg"),
|
|
144
|
+
placeholder: "即将上线",
|
|
115
145
|
},
|
|
116
146
|
{
|
|
117
147
|
key: "miniProgram",
|
|
@@ -141,6 +171,9 @@ export default {
|
|
|
141
171
|
if (item?.labelKey) return this.$jx3boxT(`jx3boxUi.footer.${item.labelKey}`, item.label || item.labelKey);
|
|
142
172
|
return item?.label || "";
|
|
143
173
|
},
|
|
174
|
+
handleLinkClick(e, item) {
|
|
175
|
+
if (!item?.href) e.preventDefault();
|
|
176
|
+
},
|
|
144
177
|
},
|
|
145
178
|
created: function () {},
|
|
146
179
|
mounted: function () {},
|
package/src/header/box2.vue
CHANGED
|
@@ -92,11 +92,16 @@ export default {
|
|
|
92
92
|
return this.data.filter((item) => {
|
|
93
93
|
const currentClient = String(this.client || "").toLowerCase();
|
|
94
94
|
const rawClient = item.client;
|
|
95
|
-
const
|
|
96
|
-
?
|
|
95
|
+
const clients = !rawClient
|
|
96
|
+
? []
|
|
97
97
|
: Array.isArray(rawClient)
|
|
98
|
-
? rawClient.map((c) => String(c || "").toLowerCase())
|
|
99
|
-
: String(rawClient || "")
|
|
98
|
+
? rawClient.map((c) => String(c || "").toLowerCase())
|
|
99
|
+
: String(rawClient || "")
|
|
100
|
+
.split(",")
|
|
101
|
+
.map((c) => String(c || "").trim().toLowerCase())
|
|
102
|
+
.filter(Boolean);
|
|
103
|
+
const matchedClient =
|
|
104
|
+
!clients.length || clients.includes("all") || clients.includes(currentClient);
|
|
100
105
|
return matchedClient;
|
|
101
106
|
});
|
|
102
107
|
},
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import CommonFooter from '../../CommonFooter.vue';
|
|
2
|
+
|
|
3
|
+
const meta = {
|
|
4
|
+
title: 'Layout/CommonFooter',
|
|
5
|
+
component: CommonFooter,
|
|
6
|
+
parameters: {
|
|
7
|
+
layout: 'fullscreen',
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
export const Default = {
|
|
14
|
+
render: () => ({
|
|
15
|
+
components: { CommonFooter },
|
|
16
|
+
template: `
|
|
17
|
+
<div style="min-height: 100vh; display: flex; align-items: flex-end; background: #111827;">
|
|
18
|
+
<CommonFooter style="width: 100%;" />
|
|
19
|
+
</div>
|
|
20
|
+
`,
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import CommonHeader from '../../CommonHeader.vue';
|
|
2
|
+
|
|
3
|
+
const meta = {
|
|
4
|
+
title: 'Layout/CommonHeader',
|
|
5
|
+
component: CommonHeader,
|
|
6
|
+
parameters: {
|
|
7
|
+
layout: 'fullscreen',
|
|
8
|
+
},
|
|
9
|
+
argTypes: {
|
|
10
|
+
overlayEnable: { control: 'boolean' },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
export const Default = {
|
|
17
|
+
args: {
|
|
18
|
+
overlayEnable: false,
|
|
19
|
+
},
|
|
20
|
+
render: (args) => ({
|
|
21
|
+
components: { CommonHeader },
|
|
22
|
+
setup() {
|
|
23
|
+
return { args };
|
|
24
|
+
},
|
|
25
|
+
template: `
|
|
26
|
+
<div style="min-height: 200vh; background: linear-gradient(180deg, #0b1220 0%, #1f2937 100%);">
|
|
27
|
+
<CommonHeader v-bind="args" />
|
|
28
|
+
<div style="height: 1600px;"></div>
|
|
29
|
+
</div>
|
|
30
|
+
`,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Overlay = {
|
|
35
|
+
args: {
|
|
36
|
+
overlayEnable: true,
|
|
37
|
+
},
|
|
38
|
+
render: Default.render,
|
|
39
|
+
};
|