feat(config): 添加公共目录并配置环境变量

- 在 Vite 配置中添加 publicDir 属性,指向 'public' 文件夹
- 在 Dockerfile 中设置 Vite 环境变量,用于开发和生产环境
- 更新 package.json,添加 CodeMirror 和 Milkdown 相关依赖,用于编辑器功能
This commit is contained in:
danial
2025-01-19 19:49:25 +08:00
parent 1e7928e766
commit 7977885580
12 changed files with 3085 additions and 32 deletions

View File

@@ -33,6 +33,7 @@ export default ({}: ConfigEnv): UserConfigExport => {
define: {
'process.env': {}
},
publicDir: 'public',
optimizeDeps: {
include: ['mitt', 'dayjs', 'axios', 'pinia', '@vueuse/core'],
exclude: ['@iconify-icons/lets-icons']

View File

@@ -17,6 +17,8 @@ FROM nginx:latest
# 替换nginx中的地址
# ARG NGINX_CONFIG_URL
ENV NGINX_CONFIG_URL=kami_backend
ENV VITE_ENV_SHOP_ADDR=127.0.0.1:12305
ENV VITE_ENV_GATEWAY_ADDR=127.0.0.1:12309
WORKDIR /app
# 替换默认的配置文件

View File

@@ -41,8 +41,20 @@
},
"dependencies": {
"@arco-design/web-vue": "^2.56.3",
"@codemirror/commands": "^6.8.0",
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/language": "^6.10.8",
"@codemirror/language-data": "^6.5.1",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.36.2",
"@milkdown/kit": "^7.6.1",
"@milkdown/plugin-emoji": "^7.6.1",
"@milkdown/theme-nord": "^7.6.1",
"@milkdown/vue": "^7.6.1",
"@vueuse/core": "^10.11.1",
"axios": "^1.7.9",
"codemirror": "^6.0.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^5.5.1",
@@ -66,6 +78,7 @@
"@types/crypto-js": "^4.2.2",
"@types/lodash": "^4.17.13",
"@types/mockjs": "^1.0.10",
"@types/node": "^22.10.7",
"@types/nprogress": "^0.2.3",
"@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^8.18.1",

2389
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/assets/demo.zip Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

431
src/assets/markdown/api.md Normal file
View File

@@ -0,0 +1,431 @@
# 对接文档
> AppKey**{{.payKey}}**
> AppSecret:**{{.paySecret}}**
## 1.请求支付页面
1.DEMO文件链接[Python demo文件](/public/assets/demo.zip)
请求卡密支付页面,页面如下:
![img.png](/public/images/pay/AB4467C3-FA86-069A-99BB-C3997694311C.png)
### 请求参数
地址http(s)://{{.shopHost}}/?sign=xxx
方法GET
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
| --------- | ------ | -------- | -------- | ------------------------------------------------------------------- |
| sign | string | 是 | 签名 | 6634ddd39796e4e0a98d6f5e919ae50ddc82edbf255f15aa2e30c5c70381e7b9648 |
| returnUrl | string | 否 | 返回地址 | 订单完成(成功或失败)后的返回地址 https://baidu.com/notify |
### sign生成算法
> 注意:生成签名时,需要提前按照参数的参数名进行排序,按照 a-z 顺序排序
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
| ------------- | ------ | -------- | ------------------ | ------------------------------- |
| payKey | string | 是 | 支付 key | 20190520123456789 |
| generatedTime | number | 是 | 支付时间 | 默认为 10 位当前时间(单位/秒) |
| duration | number | 是 | 订单存续时间 | 默认为 24 小时 |
| orderNo | string | 是 | 订单号(自定义生成) | 20190520123456789 |
| notifyUrl | string | 是 | 回调地址 | https://baidu.com/notify |
| showMMValue | string | 是 | 卡片面额 | 100 |
| productCode | string | 是 | 卡片编码 | 苹果卡 |
### 通道编码
> 通道编码需参考后台配置,以下仅作为示例
| 通道 | 编码 | 说明 |
| ---------------------------- | ---- | -------------------------------- |
| 苹果充值卡 | 8545 | 苹果充值卡面,由卡号和卡密组成 |
| 沃尔玛充值卡 | 8645 | 沃尔玛充值卡面,有卡号和卡密组成 |
| 天猫店铺充值兑换\|确认收货版 | 8745 | 天猫店铺充值,用户秒收货秒回调 |
| 天猫店铺充值兑换\|好评版 | 8645 | 天猫店铺充值,用户秒好评秒回调 |
### 加密算法
需要将上述参数加密生成 sign 参数sign 参数需要拼接在 url
后面,如:`http://121.37.253.228:12305/?sign=6634ddd39796e4e0a98d6f5e919ae50ddc82edbf255f15aa2e30c5c70381e7b96489f102aa574e70a4ea1b874685dfafec957b237b02d5980f0a22139d860c7d7a8d5c58e3dfbde606a0de08186a4d45228bcc1917a35768f1903bee921b78b118e8b31d25642737cb3e094c140c81f7&showMM=100&linkID=736797719398`
sign 加密算法为 AES/CBC 加密,数据填充模式为 PKCS7 填充,加密 key 和 iv 如下:
> key = thisis32bitlongpassphraseimusing
> iv = 1234567890123456
##### 加密算法参考
```go
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"encoding/json"
"fmt"
)
type OrderParams struct {
OrderNo string `json:"orderNo"`
PayKey string `json:"payKey"` // 支付明文秘钥
NotifyUrl string `json:"notifyUrl"` // 回调地址
GeneratedTime int64 `json:"generatedTime"` // 过期时间
Duration int `json:"duration"` // 存续时间(小时)
ProductCode string `json:"productCode"`
ShowMMValue float64 `json:"showMMValue"`
}
func String(p *OrderParams) string {
r, err := json.Marshal(p)
if err != nil {
return ""
}
logs.Info(string(r))
return string(r)
}
func Encrypt(p *OrderParams) string {
prepareData := String(p)
print(prepareData, "\n")
if prepareData == "" {
return prepareData
}
// 加密密码数据
key := "thisis32bitlongpassphraseimusing"
iv := "1234567890123456"
result, _ := AesCBCEncrypt([]byte(prepareData), []byte(key), []byte(iv))
return hex.EncodeToString(result)
}
// AesCBCEncrypt AES/CBC/PKCS7Padding 加密
func AesCBCEncrypt(plaintext []byte, key []byte, iv []byte) ([]byte, error) {
// AES
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// PKCS7 填充
plaintext = paddingPKCS7(plaintext, aes.BlockSize)
// CBC 加密
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(plaintext, plaintext)
return plaintext, nil
}
// PKCS7 填充
func paddingPKCS7(plaintext []byte, blockSize int) []byte {
paddingSize := blockSize - len(plaintext)%blockSize
paddingText := bytes.Repeat([]byte{byte(paddingSize)}, paddingSize)
return append(plaintext, paddingText...)
}
func main() {
orderParams := OrderParams{
OrderNo: "20190520123456789",
PayKey: "20190520123456789",
NotifyUrl: "https://baidu.com/notify",
GeneratedTime: 1558312000,
Duration: 24,
ProductCode: "8645",
ShowMMValue: 100,
}
sign := Encrypt(&orderParams)
// 原始本文
fmt.Println(sign)
}
// 原始文本:{"orderNo":"20190520123456789","payKey":"20190520123456789","notifyUrl":"https://baidu.com/notify","generatedTime":1558312000,"duration":24,"productCode":"8645","showMMValue":100}
// aes加密后的文本94db297041a61fa78ac2176b9e8e1e9b34d2ae33b6d46fc8c731a34b4c2b76eea7738993c2c175e6d61648c31c00b5d6d465246aa39b5fba5d6b77599f112c791370486b9fb19ad675e228b1d36ec6ad890ac692a262a73f126dda2bffae493452ea20d8604874af1ff2ba67cf6aab5c5821d100beec4d4c4de86d4c014fc8fe0bfe61b259752007f498dbee92f0136b2cbb756ff5405f95fc7763405022c835812f58dda86c9d72f84c6d576d2e98909d0e499e3c4b4da552f478bfbce3ba63
// 签名key=thisis32bitlongpassphraseimusing iv=1234567890123456
// 算法CBC非对称加密/PKCS7填充
```
## 2.请求实际支付金额(可选)
> 通过面额获取实际支付金额。注意,实际支付面额可能会随时修改,建议每次拉取订单前重新获取最新面额,切勿缓存。
> 当前接口会返回当前面额在各个平台设置的实际支付金额,以及设置各个平台的链接,根据需要获取。
接口http(s)://{{.remoteHost}}/gateway/getAllowedMM
请求方式GET
| 参数名称 | 类型 | 是否必须 | 描述 | 实例 |
| ----------- | ------ | -------- | -------- | ----------------- |
| showMMValue | string | 是 | 展示面额 | 充值卡上的面额 |
| payKey | string | 是 | 支付 key | 20190520123456789 |
| productCode | string | 卡片编码 | 是 | 8456 |
#### 返回参数
| 参数名称 | 参数类型 | 描述 | 说明 |
| -------- | --------- | ------------ | --------------- |
| code | int | 平台参数 | 0 成功 其他失败 |
| msg | string | 信息提示 | 错误原因 |
| data | interface | 具体返回数据 | 返回数据如下 |
具体成功返回数据
| 参数名称 | 参数类型 | 描述 | 说明 |
| --------- | -------- | -------------- | ---- |
| showLabel | string | 卡面面值 | |
| factLabel | string | 实际面值 | |
| linkID | string | 需要下单的链接 | |
## 3.提交订单(创建订单)
> 提交订单接口,此接口用于第三方直接提交卡号/卡密,使用自有拉单页面
接口http(s)://{{.remoteHost}}/gateway/scan
请求方式POST
### 请求参数
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
| ----------- | ------ | -------- | ------------------ | --------------------------------------------------- |
| exValue | string | 是 | 扩展参数 | 卡密数据(具体格式如下) |
| orderNo | string | 是 | 订单号 | 20190520123456789 |
| orderPeriod | int | 否 | 订单有效期(小时) | 24 |
| orderPrice | float | 是 | 订单价格 | 100.12 |
| notifyUrl | string | 是 | 回调地址 | https://baidu.com/notify?sign=xxx |
| payKey | string | 是 | 商户秘钥 | |
| timestamp | int | 是 | 时间戳 | Unix 时间戳,时区为 GMT+8 允许与服务器最大误差 30 秒 |
| sign | string | 是 | 签名 | 签名算法如下 |
| productCode | string | 卡片编码 | 是 | 8456 |
| ip | string | 否 | 客户端IP | 127.0.0.1 |
| deviceId | string | 否 | 设备唯一标识 | 127.0.0.1 |
### 参数说明
#### exValue
内需参数
| 参数名称 | 参数类型 | 描述 | 说明 |
| ------------ | -------- | -------- | ---------------------- |
| data | string | 卡密 | --- |
| cardNo | string | 面额 | --- |
| recoveryType | string | 回收类型 | 2.仅有卡密 8.卡密/卡号 |
### 返回参数
返回参数
| 参数名称 | 参数类型 | 描述 | 说明 |
| -------- | --------- | ------------ | --------------- |
| code | int | 平台参数 | 0 成功 其他失败 |
| msg | string | 信息提示 | |
| data | interface | 具体返回数据 | 返回数据如下 |
具体成功返回数据
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
| ---------- | ------ | -------- | ------------ | ----------------- |
| statusCode | string | 是 | 返回状态码 | 00 成功,其他失败 |
| orderNo | string | 是 | 订单号 | 20190520123456789 |
| orderPrice | string | 是 | 订单价格 | 100.12 |
| sign | string | 是 | 签名 | xxx |
| msg | string | 是 | 附带返回信息 | 成功 |
## 4.查询订单
地址http(s)://{{.remoteHost}}/gateway/merchant/query
方法Get
查询参数
| 参数名称 | 参数类型 | 是否必须 | 描述 | 说明 |
| --------- | -------- | -------- | -------- | --------------------------------------------------- |
| orderNo | string | 是 | 订单号 | |
| appKey | string | 是 | 应用 key | |
| timestamp | int | 是 | 时间戳 | Unix 时间戳,时区为 GMT+8 允许与服务器最大误差 30 秒 |
| sign | string | 是 | 签名 | xxx |
### 返回参数
| 参数名称 | 参数类型 | 描述 | 说明 |
| -------- | --------- | ------------ | --------------- |
| code | int | 平台参数 | 0 成功 其他失败 |
| msg | string | 信息提示 | |
| data | interface | 具体返回数据 | 返回数据如下 |
具体成功返回数据
| 参数名称 | 参数类型 | 描述 | 说明 |
| -------- | -------- | -------- | ------------------- |
| orderNo | string | 订单号 | |
| cardNo | string | 卡号 | |
| cardPwd | string | 卡密 | |
| status | string | 订单状态 | successfail, wait |
| faceVal | float | 面额 | |
| amount | string | 实际面额 | |
## 4.创建订单(新)
> 创建订单,并返回下单单号,地址等信息
地址http(s)://{{.remoteHost}}/gateway/createOrder
### 请求参数
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
| ----------- | ------ | -------- | ------------------ | --------------------------------------------------- |
| exValue | string | 是 | 扩展参数 | 卡密数据(具体格式如下) |
| orderNo | string | 是 | 订单号 | 20190520123456789 |
| orderPeriod | int | 否 | 订单有效期(小时) | 24 |
| orderPrice | float | 是 | 订单价格 | 100.12 |
| notifyUrl | string | 是 | 回调地址 | https://baidu.com/notify?sign=xxx |
| payKey | string | 是 | 商户秘钥 | |
| timestamp | int | 是 | 时间戳 | Unix 时间戳,时区为 GMT+8 允许与服务器最大误差 30 秒 |
| sign | string | 是 | 签名 | 签名算法如下 |
| productCode | string | 卡片编码 | 是 | 8456 |
| timestamp | int | 是 | 时间戳 | Unix 时间戳,时区为 GMT+8 允许与服务器最大误差 30 秒 |
### 返回参数
| 参数名称 | 参数类型 | 描述 | 说明 |
| -------- | --------- | ------------ | --------------- |
| code | int | 平台参数 | 0 成功 -1失败 |
| msg | string | 信息提示 | 失败的具体原因 |
| data | interface | 具体返回数据 | 返回数据如下 |
具体成功返回数据
| 参数名称 | 参数类型 | 描述 | 说明 |
| -------- | -------- | -------- | ------------------- |
| productCode | string | 产品编码 | |
| paymentName | string | 产品名称 | |
| transactionType | string | 卡密 | 是兑换还是充值 暂时无用 |
| payUrl | string | 支付链接 | 最终构建的支付链接
| merchantOrderNo | string | 商户订单号 | 来源自请求传入订单号 |
| orderNo | string | 订单编号 | 系统编号 |
#### 签名算法sign
> 注意:
> 1 对参数进行签名时,一定不要写死!!!,请按签名算法进行计算!!
> 2 所有提交参数都需要参与签名计算 为空的也需要
##### 参数签名算法示例算法
请注意: 1.对参数进行签名时,一定不要写死!!!,请按签名算法进行计算!! 2.所有提交参数都需要参与签名计算 为空的也需要3.下面的参数只是示例,具体参数要根据实际接口参数进行验签
```go
package main
import (
"crypto/md5"
"encoding/hex"
"sort"
"strconv"
"time"
)
func main() {
//实际获取的appKey值
appKey := "1f2811dcd32b2c5c"
//实际获取的appSecret值
appSecret := "3C569A210D1260B2"
// 如果有其它参数,依次添加 key=value
params := map[string]string{}
params["appKey"] = appKey
params["phone"] = "13800138000"
params["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
params["amount"] = "100"
strArr := SortMap(params)
signStr := ""
for i := 0; i < len(strArr); i++ {
k := strArr[i]
if len(params[k]) == 0 {
signStr += k
} else {
signStr += k + params[k]
}
}
signStr += appSecret
h := md5.New()
h.Write([]byte(signStr))
hex.EncodeToString(h.Sum(nil))
}
// SortMap 对map的key值进行排序
func SortMap(m map[string]string) []string {
var arr []string
for k := range m {
arr = append(arr, k)
}
sort.Strings(arr)
return arr
}
// 原始值
// {
// "amount": "100",
// "appKey": "1f2811dcd32b2c5c",
// "phone": "13800138000",
// "timestamp": "1737284505"
// }
// appSecret3C569A210D1260B2
// 加密前的值
// amount100appKey1f2811dcd32b2c5cphone13800138000timestamp17372845053C569A210D1260B2
// 加密后的值96ff597f7b89322e83e234e3ad1d42e5
```
Java 算法
```Java
String appSecret = "3C569A210D1260B2";
TreeMap<String, String> params = new TreeMap<String,String>();
params.put("timestamp", ""+(System.currentTimeMillis()/1000L));
params.put("phone", "13800138000");
params.put("amount", "100");
StringBuilder sb = new StringBuilder();
for(Entry<String, String> item : params.entrySet()){
if(item.getKey().equals("sign"))
continue;
sb.append(item.getKey()).append(item.getValue());
}
sb.append(appSecret);
params.put("sign",md5(sb.toString()));
```
## 订单回调接口签名检验
### 回调接口示例
地址http://baidu.com/notify?param1=xx&param2=xx
方法Get
返回参数:
| 参数名称 | 参数类型 | 含义 | 示例 |
| ----------- | -------- | -------------------------- | ----------------- |
| orderNo | string | 订单号(己方账号) | 20190520123456789 |
| orderPrice | string | 订单价格(面值,一般为整数) | 100 |
| factPrice | string | 实际价格 | 98.89 |
| orderTime | string | 回调时间 | 2006010215:04:05 |
| trxNo | string | 交易(系统账户)流水号 | 20190520123456789 |
| payKey | string | 支付通道 key | 20190520123456789 |
| sign | string | 签名 | xxx |
| tradeStatus | string | 交易状态 | success |
| statusCode | string | 订单状态 | success |
### 签名算法
签名算法参见`提交订单(创建订单)`接口 ↑

View File

@@ -0,0 +1,179 @@
milkdown-code-block {
display: block;
position: relative;
padding: 24px 40px;
background: #f5f5f5;
margin: 20px 0;
.language-picker {
width: max-content;
position: absolute;
z-index: 1;
display: none;
padding-top: 16px;
}
.language-picker.show {
display: block;
}
.language-button {
display: flex;
align-items: center;
}
.search-box {
display: flex;
align-items: center;
}
.search-box .clear-icon {
cursor: pointer;
}
.hidden {
display: none;
}
.cm-editor {
outline: none !important;
}
.language-button {
gap: 8px;
padding: 8px;
background: antiquewhite;
border-radius: 8px;
font-size: 14px;
margin-bottom: 24px;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
&:hover .language-button {
opacity: 1;
}
.language-button:hover {
background: bisque;
}
.language-button .expand-icon {
transition: transform 0.2s ease-in-out;
}
.language-button .expand-icon svg {
width: 16px;
height: 16px;
}
.language-button[data-expanded='true'] .expand-icon {
transform: rotate(180deg);
}
.language-button .expand-icon svg:focus,
.language-button .expand-icon:focus-visible {
outline: none;
}
.list-wrapper {
background: antiquewhite;
border-radius: 16px;
box-shadow:
0px 2px 6px 0px rgba(0, 0, 0, 0.15),
0px 1px 2px 0px rgba(0, 0, 0, 0.3);
width: 220px;
}
.language-list {
height: 356px;
overflow-y: auto;
margin: 0;
padding: 0;
}
.language-list-item {
cursor: pointer;
margin: 0;
height: 32px;
display: flex;
align-items: center;
gap: 8px;
padding: 0 8px;
font-size: 12px;
}
.language-list-item:hover {
background: cornsilk;
}
.language-list-item:focus-visible {
outline: none;
background: cornsilk;
}
.language-list-item .leading,
.language-list-item .leading svg {
width: 16px;
height: 16px;
}
.list-wrapper {
padding-top: 20px;
}
.search-box {
margin: 0 16px 12px;
background: white;
height: 32px;
border-radius: 4px;
outline: 2px solid fuchsia;
gap: 8px;
padding: 0 16px;
font-size: 12px;
}
.search-box .search-input {
width: 100%;
}
.search-box .search-icon svg {
width: 16px;
height: 16px;
}
.search-box .clear-icon svg {
width: 16px;
height: 16px;
}
.search-box input {
background: transparent;
}
.search-box input:focus {
outline: none;
}
}
blockquote {
/* 添加背景 */
background: #e2e2e2;
padding: 8px 4px;
margin: 16px 0;
/* 前方添加一个小柱状体 */
position: relative;
padding-left: 16px;
border-left: 4px solid var(--color-primary-light-2);
}
.milkdown {
/* 设置字间距 */
line-height: 2;
/* 设置字间距 */
letter-spacing: 0.05em;
/* 设置字大小 */
font-size: 16px;
font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Helvetica,
Arial, sans-serif;
}

View File

@@ -11,6 +11,15 @@ const IFRAME: AppRouteRecordRaw = {
requiresAuth: false
},
children: [
{
path: 'apiDoc',
name: 'apiDoc',
component: () => import('@/views/api-doc/index.vue'),
meta: {
locale: 'API文档',
requiresAuth: false
}
},
{
path: 'merchantConfig',
name: 'iframeMerchantConfig',

View File

@@ -0,0 +1,79 @@
<script setup lang="ts">
import { Milkdown, useEditor } from '@milkdown/vue';
import {
defaultValueCtx,
Editor,
editorViewOptionsCtx,
rootCtx
} from '@milkdown/kit/core';
import {
codeBlockComponent,
codeBlockConfig
} from '@milkdown/kit/component/code-block';
import { html } from '@milkdown/kit/component';
import { nord } from '@milkdown/theme-nord';
import doc from 'assets/markdown/api.md?raw';
import { commonmark } from '@milkdown/kit/preset/commonmark';
import { emoji } from '@milkdown/plugin-emoji';
import { basicSetup } from 'codemirror';
import { oneDark } from '@codemirror/theme-one-dark';
import { defaultKeymap } from '@codemirror/commands';
import { keymap } from '@codemirror/view';
import { languages } from '@codemirror/language-data';
import '@milkdown/theme-nord/style.css';
import 'assets/style/markdown.css';
import { gfm } from '@milkdown/kit/preset/gfm';
let myDoc = doc
.split('{{.remoteHost}}')
.join(process.env.VITE_ENV_GATEWAY_ADDR);
myDoc = doc.split('{{.shopHost}}').join(process.env.VITE_ENV_SHOP_ADDR);
const check = html`
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
`;
useEditor(root => {
return Editor.make()
.config(nord)
.config(ctx => {
ctx.set(rootCtx, root);
ctx.set(defaultValueCtx, myDoc);
ctx.update(editorViewOptionsCtx, prev => ({
...prev,
editable: () => false
}));
ctx.update(codeBlockConfig.key, defaultConfig => ({
...defaultConfig,
languages,
extensions: [basicSetup, oneDark, keymap.of(defaultKeymap)],
renderLanguage: (language, selected) => {
return html`
<span class="leading">${selected ? check : null}</span>
${language}
`;
}
}));
})
.use(emoji)
.use(gfm)
.use(codeBlockComponent)
.use(commonmark);
});
</script>
<template>
<Milkdown />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import Milkdown from './component.vue';
import { MilkdownProvider } from '@milkdown/vue';
</script>
<template>
<div class="container">
<MilkdownProvider>
<Milkdown />
</MilkdownProvider>
</div>
</template>

View File

@@ -29,7 +29,7 @@
"@/*": ["src/*"],
"@config/*": ["config/*"]
},
"types": ["vite/client"]
"types": ["vite/client", "node"]
},
"include": ["src/**/*", "src/**/*.vue", "vite.config.ts"],
"exclude": ["node_modules", "dist", "**/*.js"]