Compare commits

..

11 Commits

Author SHA1 Message Date
Timi
4ae8904697 add FormatText.ts 2026-04-10 12:42:53 +08:00
Timi
5472d00154 remove default font 2026-04-09 23:41:57 +08:00
Timi
3a415c2d66 fix export style 2026-04-08 00:49:21 +08:00
Timi
50b045c1db add transparent tui-color 2026-04-08 00:49:04 +08:00
Timi
b90e4fc43b fix api export 2026-04-08 00:48:05 +08:00
Timi
1421416983 support code for markdown-editor 2026-04-02 16:56:04 +08:00
Timi
ff50a7d7a9 text cursor for selectable 2026-04-02 16:54:13 +08:00
Timi
dfbd357422 constraint key of T in Page.orderMap 2026-04-02 16:53:37 +08:00
Timi
c266c9e95b fix import path 2026-04-02 16:52:48 +08:00
Timi
cdaf858a53 remove .npmrc 2026-02-19 18:37:32 +08:00
Timi
8e9ba23f72 fix Comment Page 2026-01-15 15:22:07 +08:00
50 changed files with 378 additions and 294 deletions

17
.gitignore vendored
View File

@@ -1,3 +1,11 @@
/.npmrc
/.eslintrc-auto-import.json
/components.d.ts
/examples/auto-imports.d.ts
/CLAUDE.md
/AGENTS.md
/.claude
# Logs # Logs
logs logs
*.log *.log
@@ -22,12 +30,3 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
/.eslintrc-auto-import.json
/components.d.ts
/examples/auto-imports.d.ts
/CLAUDE.md
/AGENTS.md
/.claude

1
.npmrc
View File

@@ -1 +0,0 @@
shamefully-hoist=true

View File

@@ -1,21 +1,18 @@
<template> <template>
<div class="root"> <div class="root">
<h2 v-popup="`提示`">test popup</h2> <copyright />
<icon name="ZIP" :scale="2" disabled />
<markdown-editor class="editor" v-model:data="data" :minRows="12" :max-rows="24" />
</div> </div>
<popup />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Icon, Popup } from "timi-web"; import { Copyright } from "timi-web";
import MarkdownEditor from "~/components/markdown-editor/index.vue";
const data = ref("");
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.root { .root {
width: 80%; width: 80%;
.sp {
height: 520px;
}
} }
</style> </style>

View File

@@ -1,10 +1,7 @@
import { createApp } from "vue"; import { createApp } from "vue";
import Root from "./Root.vue"; import Root from "./Root.vue";
import TimiWebUI, { axios, VPopup } from "timi-web"; // 本地开发 import { VPopup } from "timi-web"; // 本地开发
axios.defaults.baseURL = "http://localhost:8091";
const app = createApp(Root); const app = createApp(Root);
app.use(TimiWebUI);
app.directive("popup", VPopup); app.directive("popup", VPopup);
app.mount("#root"); app.mount("#root");

View File

@@ -3,15 +3,19 @@
"main": "./dist/timi-web.umd.js", "main": "./dist/timi-web.umd.js",
"types": "./dist/src/index.d.ts", "types": "./dist/src/index.d.ts",
"module": "./dist/timi-web.mjs", "module": "./dist/timi-web.mjs",
"style": "./dist/style.css", "style": "./dist/timi-web.css",
"private": false, "private": false,
"version": "1.0.0", "version": "0.0.3",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"dev:doc": "pnpm run -C docs dev", "dev:doc": "pnpm run -C docs dev",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
"build:doc": "pnpm run -C docs build" "build:doc": "pnpm run -C docs build",
"pub": "pnpm run build && npm publish --registry=https://nexus.imyeyu.com/repository/npm-timi/"
},
"publishConfig": {
"registry": "https://nexus.imyeyu.com/repository/npm-timi/"
}, },
"files": [ "files": [
"dist/**", "dist/**",
@@ -32,7 +36,7 @@
"node": ">=16.0.0" "node": ">=16.0.0"
}, },
"dependencies": { "dependencies": {
"axios": "1.12.0", "axios": "1.13.5",
"less": "4.5.1", "less": "4.5.1",
"marked": "^17.0.1", "marked": "^17.0.1",
"marked-gfm-heading-id": "^4.1.3", "marked-gfm-heading-id": "^4.1.3",

43
pnpm-lock.yaml generated
View File

@@ -9,8 +9,8 @@ importers:
.: .:
dependencies: dependencies:
axios: axios:
specifier: 1.12.0 specifier: 1.13.5
version: 1.12.0 version: 1.13.5
less: less:
specifier: 4.5.1 specifier: 4.5.1
version: 4.5.1 version: 4.5.1
@@ -539,66 +539,79 @@ packages:
resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.55.1': '@rollup/rollup-linux-arm-musleabihf@4.55.1':
resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.55.1': '@rollup/rollup-linux-arm64-gnu@4.55.1':
resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.55.1': '@rollup/rollup-linux-arm64-musl@4.55.1':
resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.55.1': '@rollup/rollup-linux-loong64-gnu@4.55.1':
resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.55.1': '@rollup/rollup-linux-loong64-musl@4.55.1':
resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.55.1': '@rollup/rollup-linux-ppc64-gnu@4.55.1':
resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.55.1': '@rollup/rollup-linux-ppc64-musl@4.55.1':
resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.55.1': '@rollup/rollup-linux-riscv64-gnu@4.55.1':
resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.55.1': '@rollup/rollup-linux-riscv64-musl@4.55.1':
resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.55.1': '@rollup/rollup-linux-s390x-gnu@4.55.1':
resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.55.1': '@rollup/rollup-linux-x64-gnu@4.55.1':
resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.55.1': '@rollup/rollup-linux-x64-musl@4.55.1':
resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.55.1': '@rollup/rollup-openbsd-x64@4.55.1':
resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==}
@@ -888,8 +901,8 @@ packages:
asynckit@0.4.0: asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
axios@1.12.0: axios@1.13.5:
resolution: {integrity: sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==} resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==}
babel-plugin-prismjs@2.1.0: babel-plugin-prismjs@2.1.0:
resolution: {integrity: sha512-ehzSKYfeAz4U78zi/sfwsjDPlq0LvDKxNefcZTJ/iKBu+plsHsLqZhUeGf1+82LAcA35UZGbU6ksEx2Utphc/g==} resolution: {integrity: sha512-ehzSKYfeAz4U78zi/sfwsjDPlq0LvDKxNefcZTJ/iKBu+plsHsLqZhUeGf1+82LAcA35UZGbU6ksEx2Utphc/g==}
@@ -1218,8 +1231,8 @@ packages:
flatted@3.3.1: flatted@3.3.1:
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
follow-redirects@1.15.6: follow-redirects@1.15.11:
resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
peerDependencies: peerDependencies:
debug: '*' debug: '*'
@@ -2711,10 +2724,6 @@ snapshots:
'@vue/shared@3.5.26': {} '@vue/shared@3.5.26': {}
acorn-jsx@5.3.2(acorn@8.14.1):
dependencies:
acorn: 8.14.1
acorn-jsx@5.3.2(acorn@8.15.0): acorn-jsx@5.3.2(acorn@8.15.0):
dependencies: dependencies:
acorn: 8.15.0 acorn: 8.15.0
@@ -2772,9 +2781,9 @@ snapshots:
asynckit@0.4.0: {} asynckit@0.4.0: {}
axios@1.12.0: axios@1.13.5:
dependencies: dependencies:
follow-redirects: 1.15.6 follow-redirects: 1.15.11
form-data: 4.0.5 form-data: 4.0.5
proxy-from-env: 1.1.0 proxy-from-env: 1.1.0
transitivePeerDependencies: transitivePeerDependencies:
@@ -3047,8 +3056,8 @@ snapshots:
espree@9.6.1: espree@9.6.1:
dependencies: dependencies:
acorn: 8.14.1 acorn: 8.15.0
acorn-jsx: 5.3.2(acorn@8.14.1) acorn-jsx: 5.3.2(acorn@8.15.0)
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
esquery@1.5.0: esquery@1.5.0:
@@ -3101,7 +3110,7 @@ snapshots:
flatted@3.3.1: {} flatted@3.3.1: {}
follow-redirects@1.15.6: {} follow-redirects@1.15.11: {}
form-data@4.0.5: form-data@4.0.5:
dependencies: dependencies:
@@ -3753,14 +3762,14 @@ snapshots:
vue-eslint-parser@9.4.3(eslint@9.39.2): vue-eslint-parser@9.4.3(eslint@9.39.2):
dependencies: dependencies:
debug: 4.4.0 debug: 4.4.3
eslint: 9.39.2 eslint: 9.39.2
eslint-scope: 7.2.2 eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
espree: 9.6.1 espree: 9.6.1
esquery: 1.5.0 esquery: 1.5.0
lodash: 4.17.21 lodash: 4.17.21
semver: 7.7.1 semver: 7.7.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color

View File

@@ -1,4 +1,5 @@
import { ArticleView, axios } from "timi-web"; import { ArticleView } from "../types";
import { axios } from "./BaseAPI";
/** /**
* 获取文章 * 获取文章

16
src/api/BaseAPI.ts Normal file
View File

@@ -0,0 +1,16 @@
import Network from "../utils/Network";
let serviceAxios: any | null = null;
function getServiceAxios(): any {
if (serviceAxios === null) {
serviceAxios = Network.axios;
}
return serviceAxios;
}
export const axios = new Proxy({} as any, {
get(_target: any, prop: string | symbol, receiver: any): any {
return Reflect.get(getServiceAxios(), prop, receiver);
}
});

View File

@@ -1,27 +1,18 @@
import { import { CaptchaData, Comment, CommentReply, Page, PageResult } from "../types";
axios, import { axios } from "./BaseAPI";
CaptchaData,
Comment,
CommentPage,
CommentReply,
CommentReplyPage,
CommentReplyView,
CommentView,
PageResult
} from "~/index";
const BASE_URI = "/comment"; const BASE_URI = "/comment";
async function page(commentPage: CommentPage): Promise<PageResult<CommentView>> { async function page(page: Page<Comment>): Promise<PageResult<Comment>> {
return axios.post(`${BASE_URI}/list`, commentPage); return axios.post(`${BASE_URI}/list`, page);
} }
async function create(captchaData: CaptchaData<Comment>): Promise<void> { async function create(captchaData: CaptchaData<Comment>): Promise<void> {
return axios.post(`${BASE_URI}/create`, captchaData); return axios.post(`${BASE_URI}/create`, captchaData);
} }
async function pageReply(commentReplyPage: CommentReplyPage): Promise<PageResult<CommentReplyView>> { async function pageReply(page: Page<CommentReply>): Promise<PageResult<CommentReply>> {
return axios.post(`${BASE_URI}/reply/list`, commentReplyPage); return axios.post(`${BASE_URI}/reply/list`, page);
} }
async function createReply(captchaData: CaptchaData<CommentReply>): Promise<void> { async function createReply(captchaData: CaptchaData<CommentReply>): Promise<void> {

View File

@@ -1,4 +1,6 @@
import { axios, SettingKey, TemplateBizType, Toolkit } from "~/index"; import { TemplateBizType } from "../types";
import Toolkit from "../utils/Toolkit";
import { axios } from "./BaseAPI";
const getCaptchaAPI = () => axios.defaults.baseURL + "/captcha"; const getCaptchaAPI = () => axios.defaults.baseURL + "/captcha";

View File

@@ -1,4 +1,5 @@
import { axios, Developer } from "~/index"; import { Developer } from "../types";
import { axios } from "./BaseAPI";
const BASE_URI = "/git/developer"; const BASE_URI = "/git/developer";

View File

@@ -1,15 +1,15 @@
import { import {
Attachment, Attachment,
axios,
CaptchaData, CaptchaData,
CommonAPI,
LoginRequest, LoginRequest,
LoginResponse, LoginResponse,
RegisterRequest, RegisterRequest,
UserAttachType, UserAttachType,
UserProfileView, UserProfileView,
UserView UserView
} from "~/index"; } from "../types";
import { axios } from "./BaseAPI";
import CommonAPI from "./CommonAPI";
const BASE_URI = "/user"; const BASE_URI = "/user";

5
src/api/index.ts Normal file
View File

@@ -0,0 +1,5 @@
export { default as ArticleAPI } from "./ArticleAPI";
export { default as CommentAPI } from "./CommentAPI";
export { default as CommonAPI } from "./CommonAPI";
export { default as DeveloperAPI } from "./DeveloperAPI";
export { default as UserAPI } from "./UserAPI";

View File

@@ -1,18 +1,18 @@
@import url(~/assets/style/variable); @import url(./variable);
*::-webkit-scrollbar { .custom-scroll-bar *::-webkit-scrollbar {
width: 10px !important; width: 10px !important;
height: 10px !important; height: 10px !important;
cursor: var(--tui-cur-default); cursor: var(--tui-cur-default);
background: #CFD2E0; background: #CFD2E0;
} }
*::-webkit-scrollbar-corner { .custom-scroll-bar *::-webkit-scrollbar-corner {
background: #CFD2E0; background: #CFD2E0;
cursor: var(--tui-cur-default); cursor: var(--tui-cur-default);
} }
*::-webkit-scrollbar-thumb { .custom-scroll-bar *::-webkit-scrollbar-thumb {
cursor: var(--tui-cur-default); cursor: var(--tui-cur-default);
background: #525870; background: #525870;
} }
@@ -32,7 +32,6 @@ body {
padding: 0; padding: 0;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
font-family: var(--tui-font);
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
&::-webkit-scrollbar { &::-webkit-scrollbar {
@@ -97,7 +96,6 @@ input[type="password"] {
textarea { textarea {
tab-size: 4; tab-size: 4;
font-family: var(--tui-font);
} }
.gray-filter { .gray-filter {
@@ -185,6 +183,7 @@ textarea {
-moz-user-select: text; -moz-user-select: text;
-ms-user-select: text; -ms-user-select: text;
user-select: text; user-select: text;
cursor: text;
} }
.ir-default, .ir-default,

View File

@@ -12,6 +12,7 @@
dark-white: #E7EAEF; dark-white: #E7EAEF;
yellow: #FF0; yellow: #FF0;
purple: PURPLE; purple: PURPLE;
transparent: transparent;
} }
:root { :root {

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../../utils/Toolkit";
export const BEFlowerFall = Toolkit.withInstall(view); export const BEFlowerFall = Toolkit.withInstall(view);
export default BEFlowerFall; export default BEFlowerFall;

View File

@@ -5,8 +5,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import Toolkit from "~/utils/Toolkit"; import Resizer from "../../../utils/Resizer";
import Resizer from "~/utils/Resizer"; import Toolkit from "../../../utils/Toolkit";
defineOptions({ defineOptions({
name: "BEFlowerFall" name: "BEFlowerFall"

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const Captcha = Toolkit.withInstall(view); export const Captcha = Toolkit.withInstall(view);
export default Captcha; export default Captcha;

View File

@@ -11,7 +11,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
defineOptions({ defineOptions({
name: "Captcha" name: "Captcha"

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const Copyright = Toolkit.withInstall(view); export const Copyright = Toolkit.withInstall(view);
export default Copyright; export default Copyright;

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const EmptyTips = Toolkit.withInstall(view); export const EmptyTips = Toolkit.withInstall(view);
export default EmptyTips; export default EmptyTips;

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const Icon = Toolkit.withInstall(view); export const Icon = Toolkit.withInstall(view);
export default Icon; export default Icon;

View File

@@ -13,7 +13,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import IconMapper from "~/utils/IconMapper"; import IconMapper from "../../utils/IconMapper";
defineOptions({ defineOptions({
name: "Icon" name: "Icon"

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const Loading = Toolkit.withInstall(view); export const Loading = Toolkit.withInstall(view);
export default Loading; export default Loading;

View File

@@ -13,7 +13,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Toolkit } from "~/index"; import Toolkit from "../../utils/Toolkit";
defineOptions({ defineOptions({
name: "Loading" name: "Loading"
}); });

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const MarkdownEditor = Toolkit.withInstall(view); export const MarkdownEditor = Toolkit.withInstall(view);
export default MarkdownEditor; export default MarkdownEditor;

View File

@@ -2,11 +2,13 @@
<div <div
ref="root" ref="root"
class="tui-markdown-editor diselect" class="tui-markdown-editor diselect"
:class="{ 'fold': isFold }" :class="{
'fold': isFold,
'code': code
}"
> >
<div class="editor"> <div class="editor">
<div class="header"> <div class="header">
<icon class="icon" name="WRITING" />
<slot name="editorHeader"> <slot name="editorHeader">
<h4 class="title"> <h4 class="title">
<span>源码</span> <span>源码</span>
@@ -15,30 +17,32 @@
</slot> </slot>
</div> </div>
<slot name="editor"> <slot name="editor">
<textarea ref="textArea" class="text-area" v-model="_data"></textarea> <textarea ref="textArea" class="text-area" v-model="_data" @keydown.tab.prevent="onTab"></textarea>
</slot> </slot>
</div> </div>
<div class="preview" :class="{ 'showing': showingPreview }"> <div class="preview" :class="{ 'showing': showingPreview }">
<div class="header"> <div class="header">
<icon <div
v-if="isFold" v-if="isFold"
class="icon cur-pointer" class="icon cur-pointer"
:name="showingPreview ? 'ARROW_1_E' : 'ARROW_1_W'" v-text="showingPreview ? '>' : '<'"
@click="showingPreview = !showingPreview" @click="showingPreview = !showingPreview"
/> >
</div>
<slot name="previewHeader"> <slot name="previewHeader">
<h4 class="title">预览</h4> <h4 class="title">预览</h4>
</slot> </slot>
</div> </div>
<div ref="previewContent" class="content"> <div ref="previewContent" class="content">
<markdown-view :content="_data" /> <markdown-view v-if="code" :code="`${code}:${_data}`" />
<markdown-view v-else :content="_data" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Icon, MarkdownView } from "~/index"; import { MarkdownView } from "../markdown-view";
import calcHeight from "./CalcTextareaHeight"; import calcHeight from "./CalcTextareaHeight";
defineOptions({ defineOptions({
@@ -46,6 +50,7 @@ defineOptions({
}); });
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
code?: string;
data?: string, data?: string,
minRows?: number, minRows?: number,
maxRows?: number maxRows?: number
@@ -99,7 +104,7 @@ onMounted(() => {
const textareaHeightObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => { const textareaHeightObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
if (entries && 0 < entries.length) { if (entries && 0 < entries.length) {
if (previewContent.value) { if (previewContent.value) {
previewContent.value.style.height = entries[0].contentRect.height + "px"; previewContent.value.style.height = `calc(${entries[0].contentRect.height}px + 16px)`;
} }
} }
}); });
@@ -107,6 +112,56 @@ onMounted(() => {
} }
}); });
// ---------- Tab 缩进 ----------
const TAB = "\t";
/** 处理 Tab / Shift+Tab 缩进 */
const onTab = (e: KeyboardEvent) => {
const el = textArea.value!;
const { selectionStart: start, selectionEnd: end, value } = el;
const lines = value.split("\n");
// 计算选区覆盖的行范围
let charCount = 0;
let startLine = 0;
let endLine = 0;
for (let i = 0; i < lines.length; i++) {
const lineEnd = charCount + lines[i].length;
if (charCount <= start && start <= lineEnd) startLine = i;
if (charCount <= end && end <= lineEnd) endLine = i;
charCount += lines[i].length + 1;
}
// 单行且无选区:直接插入 Tab
if (!e.shiftKey && startLine === endLine && start === end) {
_data.value = value.slice(0, start) + TAB + value.slice(end);
nextTick(() => {
el.selectionStart = el.selectionEnd = start + 1;
});
return;
}
// 多行或有选区:整行缩进 / 反缩进
const newLines = lines.map((line, i) => {
if (i < startLine || i > endLine) return line;
if (e.shiftKey) return line.startsWith(TAB) ? line.slice(1) : line;
return TAB + line;
});
// 计算新选区位置
const delta = e.shiftKey ? -1 : 1;
const affectedCount = endLine - startLine + 1;
const newStart = Math.max(0, start + delta);
const newEnd = Math.max(newStart, end + delta * affectedCount);
_data.value = newLines.join("\n");
nextTick(() => {
el.selectionStart = newStart;
el.selectionEnd = newEnd;
});
};
defineExpose({ defineExpose({
textArea textArea
}); });
@@ -188,5 +243,21 @@ defineExpose({
} }
} }
} }
&.code {
.preview {
.content {
padding: 0;
:deep(pre[class*="language-"]) {
margin: 0;
border: none;
}
}
}
}
} }
</style> </style>

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
import "./style.less"; import "./style.less";
export const MarkdownView = Toolkit.withInstall(view); export const MarkdownView = Toolkit.withInstall(view);

View File

@@ -7,7 +7,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import Prism from "prismjs"; import Prism from "prismjs";
import Markdown from "~/utils/Markdown"; import Markdown from "../../utils/Markdown";
defineOptions({ defineOptions({
name: "MarkdownView" name: "MarkdownView"

View File

@@ -1,4 +1,4 @@
@import url(~/assets/style/variable); @import url("../../assets/style/variable");
.tui-markdown-view { .tui-markdown-view {
font-size: 14px; font-size: 14px;

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const Popup = Toolkit.withInstall(view); export const Popup = Toolkit.withInstall(view);
export default Popup; export default Popup;

View File

@@ -1,5 +1,5 @@
import view from "./index.vue"; import view from "./index.vue";
import Toolkit from "~/utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
export const UserLevel = Toolkit.withInstall(view); export const UserLevel = Toolkit.withInstall(view);
export default UserLevel; export default UserLevel;

View File

@@ -2,54 +2,17 @@ import type { App } from "vue";
import components from "./components"; import components from "./components";
import Network from "./utils/Network";
import UserAPI from "./api/UserAPI";
import CommonAPI from "./api/CommonAPI";
import ArticleAPI from "./api/ArticleAPI";
import CommentAPI from "./api/CommentAPI";
import DeveloperAPI from "./api/DeveloperAPI";
import Time from "./utils/Time";
import IOSize from "./utils/IOSize";
import Events from "./utils/Events";
import Cooker from "./utils/Cooker";
import Toolkit from "./utils/Toolkit";
import Resizer from "./utils/Resizer";
import Storage from "./utils/Storage";
import Prismjs from "./utils/Prismjs";
import Markdown from "./utils/Markdown";
import Scroller from "./utils/Scroller";
import IconMapper from "./utils/IconMapper";
import SettingMapper from "./utils/SettingMapper";
import VPopup from "./utils/directives/Popup";
import VDraggable from "./utils/directives/Draggable";
import { userStore } from "./store/user";
import { deviceStore } from "./store/device";
import "./assets/style/variable.less"; import "./assets/style/variable.less";
import "./assets/style/timi-web.less"; import "./assets/style/timi-web.less";
import { Network } from "./utils";
export * from "./api";
export * from "./components"; export * from "./components";
export * from "./store";
export * from "./types";
export * from "./utils";
export * from "./types/Model"; export const axios = Network.axios;
export * from "./types/Article";
export * from "./types/User";
export * from "./types/Comment";
export * from "./types/Setting";
export * from "./types/Template";
export * from "./types/Developer";
export * from "./types/Attachment";
export * from "./utils/Prismjs";
export * from "./utils/directives/Popup";
export type { ScrollListener } from "./utils/Scroller";
export type { DraggableConfig } from "./utils/directives/Draggable";
export type { PopupConfig } from "./utils/directives/Popup";
const install = function (app: App) { const install = function (app: App) {
components.forEach(component => { components.forEach(component => {
@@ -57,40 +20,6 @@ const install = function (app: App) {
}); });
}; };
const axios = Network.axios;
const setGlobalErrorCallback = Network.setGlobalErrorCallback;
export default { export default {
install install
}; };
export {
axios,
Network,
setGlobalErrorCallback,
UserAPI,
CommonAPI,
ArticleAPI,
CommentAPI,
DeveloperAPI,
userStore,
deviceStore,
Time,
Events,
IOSize,
Cooker,
Toolkit,
Resizer,
Storage,
Prismjs,
Markdown,
Scroller,
IconMapper,
SettingMapper,
VPopup,
VDraggable
};

View File

@@ -1,4 +1,4 @@
import { Resizer } from "~/index"; import Resizer from "../utils/Resizer";
// true 为手机 // true 为手机
const isPhone = ref(false); const isPhone = ref(false);

2
src/store/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./device";
export * from "./user";

View File

@@ -1,4 +1,6 @@
import { LoginResponse, LoginToken, LoginUser, Storage, UserAPI, UserToken } from "timi-web"; import { LoginResponse, LoginToken, LoginUser, UserToken } from "../types";
import Storage from "../utils/Storage";
import UserAPI from "../api/UserAPI";
const loginUser = reactive<LoginUser>({ const loginUser = reactive<LoginUser>({
token: undefined, token: undefined,

View File

@@ -1,4 +1,5 @@
import { AttachmentView, Model } from "timi-web"; import type { AttachmentView } from "./Attachment";
import type { Model } from "./Model";
// 文章 // 文章
export type Article<E extends ArticleMusicExtendData | ArticleSoftwareExtendData> = { export type Article<E extends ArticleMusicExtendData | ArticleSoftwareExtendData> = {

View File

@@ -3,57 +3,47 @@ import { UserView } from "./User";
/** 评论 */ /** 评论 */
export type Comment = { export type Comment = {
bizType: CommentBizType; bizType?: CommentBizType;
bizId: number; bizId?: number;
userId?: number; userId?: number;
nick?: string; nick?: string;
content: string; content?: string;
} & Model
/** 评论视图 */
export type CommentView = {
/** 所属用户 */ /** 所属用户 */
user?: UserView; user?: UserView;
/** 回复列表 */ /** 回复列表 */
replies: CommentReplyView[]; replies?: CommentReply[];
/** 回复分页 */ /** 回复分页 */
repliesPage: CommentReplyPage; repliesPage?: Page<CommentReply>;
/** 用于绑定组件当前页下标 */ /** 用于绑定组件当前页下标 */
repliesCurrent: number; repliesCurrent?: number;
/** 回复数量 */ /** 回复数量 */
repliesLength: number; repliesLength?: number;
/** 关联文章 */ /** 关联文章 */
article?: object; article?: object;
/** 关联 Git 仓库 */ /** 关联 Git 仓库 */
repository?: object; repository?: object;
} & Comment; } & Model
export type CommentReply = { export type CommentReply = {
replyId?: number; replyId?: number;
commentId: number; commentId?: number;
senderId?: number; senderId?: number;
senderNick?: string; senderNick?: string;
receiverId?: number; receiverId?: number;
receiverNick?: string; receiverNick?: string;
content: string; content?: string;
} & Model;
export type CommentReplyView = { comment?: Comment;
comment: CommentView;
sender?: UserView; sender?: UserView;
receiver?: UserView; receiver?: UserView;
} & CommentReply; } & Model;
export type CommentPage = {
bizType?: CommentBizType;
bizId?: number;
} & Page<Comment>;
export enum CommentReplyBizType { export enum CommentReplyBizType {
@@ -64,11 +54,6 @@ export enum CommentReplyBizType {
RECEIVER = "RECEIVER" RECEIVER = "RECEIVER"
} }
export type CommentReplyPage = {
bizType: CommentReplyBizType
bizId?: number
} & Page<Comment>;
export enum CommentBizType { export enum CommentBizType {
ARTICLE = "ARTICLE", ARTICLE = "ARTICLE",

View File

@@ -22,7 +22,7 @@ export type Response<T> = {
export type Page<T> = { export type Page<T> = {
index: number; index: number;
size: number; size: number;
orderMap?: { [key: string]: OrderType }; orderMap?: { [K in keyof T]?: OrderType };
equalsExample?: T; equalsExample?: T;
likesExample?: T; likesExample?: T;
} }

View File

@@ -1,5 +1,15 @@
import type { Plugin } from "vue"; import type { Plugin } from "vue";
export * from "./Model";
export * from "./Article";
export * from "./Setting";
export * from "./Attachment";
export * from "./Model";
export * from "./User";
export * from "./Template";
export * from "./Comment";
export * from "./Developer";
/** /**
* *
*/ */
@@ -8,7 +18,7 @@ export type InstallRecord<T> = T & Plugin;
/** /**
* *
*/ */
export type DefaultSlotProp = (props: {}) => unknown export type DefaultSlotProp = (props: object) => unknown
/** /**
* *

54
src/utils/FormatText.ts Normal file
View File

@@ -0,0 +1,54 @@
import IOSize from "./IOSize";
export default class FormatText {
public static keyValue(obj: object, assign: string, split: string): string {
let result = "";
Object.entries(obj).forEach(([k, v]) => result += k + assign + v + split);
return result.substring(0, result.length - split.length);
}
public static urlArgs(obj?: { [key: string]: any }): string {
if (!obj) {
return "";
}
const args: string[] = [];
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const value = obj[key];
if (Array.isArray(value)) {
value.forEach((item) => {
args.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`);
});
} else {
args.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.toString())}`);
}
}
}
return args.join("&");
}
public static template(template: string, variables: { [key: string]: any }): string {
return template.replace(/\$\{(\w+)}/g, (_, key) => variables[key]);
}
public static unit(val: number | string | null | undefined, unit: string, fixed = 0): string {
if (!val) {
return "";
}
if (typeof val === "number") {
if (Number.isNaN(val)) {
return "";
}
return val.toFixed(fixed) + unit;
}
return val.trim() + unit;
};
public static speed(val?: number) {
if (!val) {
return "";
}
return this.unit(IOSize.format(val, 2), " / s");
}
}

View File

@@ -4,7 +4,7 @@ import { markedHighlight } from "marked-highlight";
import { mangle } from "marked-mangle"; import { mangle } from "marked-mangle";
import Prism from "prismjs"; import Prism from "prismjs";
import "prismjs/themes/prism.css"; import "prismjs/themes/prism.css";
import { Toolkit } from "~/index"; import SettingMapper from "./SettingMapper";
export default class Markdown { export default class Markdown {
@@ -99,7 +99,7 @@ export default class Markdown {
} }
// 内部资源链接 // 内部资源链接
if (href.indexOf("@") !== -1) { if (href.indexOf("@") !== -1) {
href = Toolkit.toResURL(href); href = SettingMapper.toResURL(href);
} }
{ {
// 处理嵌套 markdown这可能不是最优解 // 处理嵌套 markdown这可能不是最优解
@@ -159,7 +159,7 @@ export default class Markdown {
if (extendTag) { if (extendTag) {
href = href.substring(1); href = href.substring(1);
} }
href = Toolkit.toResURL(href); href = SettingMapper.toResURL(href);
} }
const clazzStr = clazz.join(" "); const clazzStr = clazz.join(" ");

View File

@@ -1,6 +1,8 @@
import axios, { InternalAxiosRequestConfig } from "axios"; import axios, { InternalAxiosRequestConfig } from "axios";
import { Response } from "~/types/Model"; import Cooker from "./Cooker";
import { Cooker, Time, userStore } from "~/index"; import type { Response } from "../types/Model";
import Time from "./Time";
import { userStore } from "../store/user";
type ErrorCallback = (response: Response<any>) => void; type ErrorCallback = (response: Response<any>) => void;

View File

@@ -1,7 +1,7 @@
import { SettingKey } from "~/types/Setting";
import { readonly, Ref, ref } from "vue"; import { readonly, Ref, ref } from "vue";
import CommonAPI from "~/api/CommonAPI"; import { RunEnv, SettingKey } from "../types";
import { RunEnv, Toolkit } from "timi-web"; import Toolkit from "./Toolkit";
import CommonAPI from "../api/CommonAPI";
export default class SettingMapper { export default class SettingMapper {
@@ -100,4 +100,22 @@ export default class SettingMapper {
} }
return SettingMapper.instance = new SettingMapper(); return SettingMapper.instance = new SettingMapper();
} }
/**
* 解析资源 URL 协议前缀
*
* @param val 原始值,如 `res@/path/to/file`
* @returns 完整 URL
*/
public static toResURL(val: string): string {
const at = val.indexOf("@");
const start = val.substring(0, at);
const path = val.substring(at + 1);
switch (start) {
case "res": return SettingMapper.getDomainLink(SettingKey.DOMAIN_RESOURCE) + path;
case "dl": return SettingMapper.getDomainLink(SettingKey.DOMAIN_DOWNLOAD) + path;
case "attach": return CommonAPI.getAttachmentReadAPI(path);
}
return val;
}
} }

View File

@@ -1,3 +1,5 @@
import Toolkit from "./Toolkit";
export default class Time { export default class Time {
/** 1 秒时间戳 */ /** 1 秒时间戳 */
@@ -104,6 +106,21 @@ export default class Time {
return { l, y, d, h, m, s, ms }; return { l, y, d, h, m, s, ms };
} }
public static duration(begin?: number, ms?: boolean): string {
if (!begin) {
return "";
}
const r = Time.between(new Date(begin), new Date());
const parts: string[] = [];
Toolkit.doWhere(0 < r.y, () => parts.push(`${r.y}`));
Toolkit.doWhere(0 < r.d, () => parts.push(`${r.d}`));
Toolkit.doWhere(0 < r.h, () => parts.push(`${r.h} 小时`));
Toolkit.doWhere(0 < r.m, () => parts.push(`${r.m} 分钟`));
Toolkit.doWhere(0 < r.s, () => parts.push(`${r.s}`));
Toolkit.doWhere(!!ms && 0 < r.ms, () => parts.push(`${r.ms} 毫秒`));
return parts.join(" ");
}
public static toMediaTime(seconds: number): string { public static toMediaTime(seconds: number): string {
seconds = Math.floor(seconds); seconds = Math.floor(seconds);
const hours = Math.floor(seconds / 3600); const hours = Math.floor(seconds / 3600);

View File

@@ -1,16 +1,15 @@
import type { App } from "vue"; import type { App } from "vue";
import type { InstallRecord } from "~/types"; import { InstallRecord, UserLevelType } from "../types";
import { UserLevelType } from "~/types/User";
import { CommonAPI, SettingKey, SettingMapper } from "~/index";
export default class Toolkit { export default class Toolkit {
public static isFunction = (val: any) => typeof val === "function"; public static isFunction = (val: any) => val && typeof val === "function";
public static isArray = Array.isArray; public static isArray = (val: any) => val && Array.isArray(val);
public static isString = (val: any) => typeof val === "string"; public static isString = (val: any) => val && typeof val === "string";
public static isSymbol = (val: any) => typeof val === "symbol"; public static isSymbol = (val: any) => val && typeof val === "symbol";
public static isObject = (val: any) => val !== null && typeof val === "object"; public static isObject = (val: any) => val && typeof val === "object";
public static isFile = (val: any) => val instanceof File; public static isFile = (val: any) => val instanceof File;
public static isNumber = (val: any) => val && typeof val === "number";
/** /**
* 添加安装方法 * 添加安装方法
@@ -183,7 +182,7 @@ export default class Toolkit {
while (n--) { while (n--) {
u8arr[n] = base64.charCodeAt(n); u8arr[n] = base64.charCodeAt(n);
} }
return new File([u8arr], fileName, {type: splitData[0].split(":")[1]}); return new File([u8arr], fileName, { type: splitData[0].split(":")[1] });
} }
/** /**
@@ -209,32 +208,6 @@ export default class Toolkit {
return target; return target;
} }
public static keyValueString(obj: object, assign: string, split: string): string {
let result = "";
Object.entries(obj).forEach(([k, v]) => result += k + assign + v + split);
return result.substring(0, result.length - split.length);
}
public static toURLArgs(obj?: { [key: string]: any }): string {
if (!obj) {
return "";
}
const args: string[] = [];
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const value = obj[key];
if (Array.isArray(value)) {
value.forEach((item) => {
args.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`);
});
} else {
args.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.toString())}`);
}
}
}
return args.join("&");
}
public static toObject(map: Map<any, any>): object { public static toObject(map: Map<any, any>): object {
return Array.from(map.entries()).reduce((acc, [key, value]) => { return Array.from(map.entries()).reduce((acc, [key, value]) => {
acc[key] = value ?? null; acc[key] = value ?? null;
@@ -311,7 +284,12 @@ export default class Toolkit {
}; };
} }
} }
return { exp, value: 8, percent: 1, nextLevelUp: 4096 }; return {
exp,
value: 8,
percent: 1,
nextLevelUp: 4096
};
} }
public static toFormData(root?: object): FormData { public static toFormData(root?: object): FormData {
@@ -340,41 +318,25 @@ export default class Toolkit {
return form; return form;
} }
public static toResURL(val: string): string {
const at = val.indexOf("@");
const start = val.substring(0, at);
const path = val.substring(at + 1);
switch (start) {
case "res": return SettingMapper.getDomainLink(SettingKey.DOMAIN_RESOURCE) + path;
case "dl": return SettingMapper.getDomainLink(SettingKey.DOMAIN_DOWNLOAD) + path;
case "attach": return CommonAPI.getAttachmentReadAPI(path);
}
return val;
}
public static format(template: string, variables: { [key: string]: any }): string {
return template.replace(/\$\{(\w+)}/g, (_, key) => variables[key]);
}
public static leftClickCallback(event: MouseEvent, callback: Function): void { public static leftClickCallback(event: MouseEvent, callback: Function): void {
if (event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey) { if (event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
callback(); callback();
} }
} }
public static doWhere(where: boolean | (() => boolean), func: () => void): void {
const condition = typeof where === 'function' ? where() : where;
if (condition) {
func();
}
}
public static doNotNull(arg: any, func: (arg: any) => void): void { public static doNotNull(arg: any, func: (arg: any) => void): void {
if (arg) { if (arg) {
func(arg); func(arg);
} }
} }
public static toCssSize(value: number | string): string {
if (typeof value === "number") {
return `${value}px`;
}
return value;
};
/** /**
* 设置随机间隔执行 * 设置随机间隔执行
* *
@@ -412,7 +374,13 @@ export default class Toolkit {
min?: number; min?: number;
max?: number; max?: number;
}): NodeJS.Timeout { }): NodeJS.Timeout {
const { handler, handleRate = 1, interval, min, max } = config; const {
handler,
handleRate = 1,
interval,
min,
max
} = config;
return setInterval(() => { return setInterval(() => {
if (Math.random() < handleRate) { if (Math.random() < handleRate) {
if (min !== undefined && max !== undefined) { if (min !== undefined && max !== undefined) {

23
src/utils/index.ts Normal file
View File

@@ -0,0 +1,23 @@
export { default as Cooker } from "./Cooker";
export { default as Events } from "./Events";
export { default as IOSize } from "./IOSize";
export { default as Markdown } from "./Markdown";
export { default as Network } from "./Network";
export { default as Prismjs } from "./Prismjs";
export { default as Resizer } from "./Resizer";
export { default as Scroller } from "./Scroller";
export { default as Storage } from "./Storage";
export { default as Format } from "./FormatText";
export { default as Time } from "./Time";
export { default as Toolkit } from "./Toolkit";
export { default as IconMapper } from "./IconMapper";
export { default as SettingMapper } from "./SettingMapper";
export { default as VDraggable } from "./directives/Draggable";
export { default as VPopup } from "./directives/Popup";
export * from "./MethodLocker";
export * from "./Network";
export * from "./Prismjs";
export * from "./directives/Draggable";
export * from "./directives/Popup";

View File

@@ -17,9 +17,6 @@
"DOM.Iterable" "DOM.Iterable"
], ],
"paths": { "paths": {
"~/*": [
"./src/*"
],
"@/*": [ "@/*": [
"./examples/*" "./examples/*"
], ],

View File

@@ -3,7 +3,7 @@
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node" "moduleResolution": "bundler"
}, },
"include": [ "include": [
"vite.config.ts" "vite.config.ts"

View File

@@ -12,14 +12,6 @@ const alias: Alias[] = [
find: "@", find: "@",
replacement: resolve(__dirname, "./examples") replacement: resolve(__dirname, "./examples")
}, },
{
find: "~",
replacement: resolve(__dirname, "./src")
},
{
find: "*",
replacement: resolve("")
},
{ {
find: /^timi-web(\/(es|lib))?$/, find: /^timi-web(\/(es|lib))?$/,
replacement: resolve(__dirname, "./src/index.ts") replacement: resolve(__dirname, "./src/index.ts")
@@ -52,14 +44,6 @@ export default defineConfig({
} }
}, },
minify: "terser", minify: "terser",
terserOptions: {
compress: {
// eslint-disable-next-line camelcase
drop_console: false,
// eslint-disable-next-line camelcase
drop_debugger: false
}
}
}, },
plugins: [ plugins: [
vue({ vue({