feat: udpate upload content type

This commit is contained in:
yanghao05
2025-04-18 14:45:06 +08:00
parent 2854afec53
commit ac65302601
18 changed files with 228 additions and 23 deletions

View File

@@ -3,12 +3,15 @@ package admin
import (
"quyun/app/models"
"quyun/app/requests"
"quyun/providers/ali"
"github.com/gofiber/fiber/v3"
)
// @provider
type medias struct{}
type medias struct {
oss *ali.OSSClient
}
// List medias
// @Router /v1/admin/medias [get]
@@ -18,3 +21,20 @@ func (ctl *medias) List(ctx fiber.Ctx, pagination *requests.Pagination, query *L
cond := models.Medias.BuildConditionWithKey(query.Keyword)
return models.Medias.List(ctx.Context(), pagination, cond)
}
// Show media
// @Router /v1/admin/medias/:id [get]
// @Bind id path
func (ctl *medias) Show(ctx fiber.Ctx, id int64) error {
media, err := models.Medias.GetByID(ctx.Context(), id)
if err != nil {
return ctx.SendString("Media not found")
}
url, err := ctl.oss.GetSignedUrl(ctx.Context(), media.Path)
if err != nil {
return err
}
return ctx.Redirect().To(url)
}

View File

@@ -28,6 +28,7 @@ func (ctl *posts) List(ctx fiber.Ctx, pagination *requests.Pagination, query *Li
type PostForm struct {
Title string `json:"title"`
HeadImage int64 `json:"head_image"`
Price int64 `json:"price"`
Discount int16 `json:"discount"`
Introduction string `json:"introduction"`

View File

@@ -23,8 +23,12 @@ func Provide(opts ...opt.Option) error {
}); err != nil {
return err
}
if err := container.Container.Provide(func() (*medias, error) {
obj := &medias{}
if err := container.Container.Provide(func(
oss *ali.OSSClient,
) (*medias, error) {
obj := &medias{
oss: oss,
}
return obj, nil
}); err != nil {

View File

@@ -45,6 +45,11 @@ func (r *Routes) Register(router fiber.Router) {
Query[ListQuery]("query"),
))
router.Get("/v1/admin/medias/:id", Func1(
r.medias.Show,
PathParam[int64]("id"),
))
// 注册路由组: orders
router.Get("/v1/admin/orders", DataFunc2(
r.orders.List,
@@ -81,10 +86,11 @@ func (r *Routes) Register(router fiber.Router) {
))
// 注册路由组: uploads
router.Get("/v1/admin/uploads/pre-uploaded-check/:md5.:ext", DataFunc2(
router.Get("/v1/admin/uploads/pre-uploaded-check/:md5.:ext", DataFunc3(
r.uploads.PreUploadCheck,
PathParam[string]("md5"),
PathParam[string]("ext"),
QueryParam[string]("mime"),
))
router.Post("/v1/admin/uploads/post-uploaded-action", Func1(

View File

@@ -32,10 +32,11 @@ type PreCheckResp struct {
// @Router /v1/admin/uploads/pre-uploaded-check/:md5.:ext [get]
// @Bind md5 path
// @Bind ext path
func (up *uploads) PreUploadCheck(ctx fiber.Ctx, md5, ext string) (*PreCheckResp, error) {
// @Bind mime query
func (up *uploads) PreUploadCheck(ctx fiber.Ctx, md5, ext, mime string) (*PreCheckResp, error) {
_, err := models.Medias.GetByHash(ctx.Context(), md5)
if err != nil && errors.Is(err, qrm.ErrNoRows) {
preSign, err := up.oss.PreSignUpload(ctx.Context(), fmt.Sprintf("%s.%s", md5, ext))
preSign, err := up.oss.PreSignUpload(ctx.Context(), fmt.Sprintf("%s.%s", md5, ext), mime)
if err != nil {
return nil, err
}

View File

@@ -68,9 +68,10 @@ func (r *Routes) Register(router fiber.Router) {
Query[ListQuery]("query"),
))
router.Get("/api/posts/buy/:id", Func1(
router.Get("/api/posts/buy/:id", DataFunc2(
r.posts.Buy,
PathParam[int64]("id"),
Local[*model.Users]("user"),
))
}

View File

@@ -2,6 +2,7 @@ package jobs
import (
"quyun/providers/ali"
"quyun/providers/app"
"quyun/providers/job"
"github.com/riverqueue/river"
@@ -40,9 +41,49 @@ func Provide(opts ...opt.Option) error {
}
if err := container.Container.Provide(func(
__job *job.Job,
app *app.Config,
job *job.Job,
oss *ali.OSSClient,
) (contracts.Initial, error) {
obj := &DownloadFromAliOSSWorker{
app: app,
job: job,
oss: oss,
}
if err := river.AddWorkerSafely(__job.Workers, obj); err != nil {
return nil, err
}
return obj, nil
}, atom.GroupInitial); err != nil {
return err
}
if err := container.Container.Provide(func(
__job *job.Job,
job *job.Job,
oss *ali.OSSClient,
) (contracts.Initial, error) {
obj := &ExtractAudioFromVideoWorker{
job: job,
oss: oss,
}
if err := river.AddWorkerSafely(__job.Workers, obj); err != nil {
return nil, err
}
return obj, nil
}, atom.GroupInitial); err != nil {
return err
}
if err := container.Container.Provide(func(
__job *job.Job,
app *app.Config,
job *job.Job,
oss *ali.OSSClient,
) (contracts.Initial, error) {
obj := &ExtractHeadImageFromVideoWorker{
app: app,
job: job,
oss: oss,
}
if err := river.AddWorkerSafely(__job.Workers, obj); err != nil {

View File

@@ -241,8 +241,8 @@ func (m *mediasModel) GetByID(ctx context.Context, id int64) (*model.Medias, err
m.log.Infof("sql: %s", stmt.DebugSql())
var media model.Medias
err := stmt.QueryContext(ctx, db, &media)
if err != nil {
if err := stmt.QueryContext(ctx, db, &media); err != nil {
m.log.Errorf("error querying media item by ID: %v", err)
return nil, err
}

View File

@@ -28,7 +28,7 @@ DB = 0
AccessKeyId = "LTAI5t86SjiP9zRd3q2w7jQN"
AccessKeySecret = "hV7spvJuWh8w0EEIXj8NFi2uBlF4aS"
Bucket ="rogee-test"
#Host ="abc"
Host ="https://assets.jdwan.com"
Region ="cn-beijing"
CallbackURL = "https://www.baidu.com"

View File

@@ -7,6 +7,7 @@ CREATE TABLE posts(
deleted_at timestamp,
status int2 NOT NULL DEFAULT 0,
title varchar(128) NOT NULL,
head_image int8 NOT NULL DEFAULT 0,
description varchar(256) NOT NULL,
content text NOT NULL,
price int8 NOT NULL DEFAULT 0,

View File

@@ -41,7 +41,7 @@ func Provide(opts ...opt.Option) error {
WithRegion(config.Region)
return &OSSClient{
client: oss.NewClient(cfg),
client: oss.NewClient(cfg.WithUseCName(true).WithEndpoint(*config.Host)),
internalClient: oss.NewClient(cfg.WithUseInternalEndpoint(true)),
config: &config,
}, nil

View File

@@ -3,6 +3,7 @@ package ali
import (
"context"
"strings"
"time"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
)
@@ -17,11 +18,11 @@ func (c *OSSClient) GetClient() *oss.Client {
return c.client
}
func (c *OSSClient) PreSignUpload(ctx context.Context, path string) (*oss.PresignResult, error) {
func (c *OSSClient) PreSignUpload(ctx context.Context, path, mimeType string) (*oss.PresignResult, error) {
request := &oss.PutObjectRequest{
Bucket: oss.Ptr(c.config.Bucket),
Key: oss.Ptr("quyun/" + strings.Trim(path, "/")),
ContentType: oss.Ptr("multipart/form-data"),
ContentType: oss.Ptr(mimeType),
}
return c.client.Presign(ctx, request)
}
@@ -38,3 +39,17 @@ func (c *OSSClient) Download(ctx context.Context, path, dest string) error {
}
return nil
}
// GetSignedUrl
func (c *OSSClient) GetSignedUrl(ctx context.Context, path string) (string, error) {
request := &oss.GetObjectRequest{
Bucket: oss.Ptr(c.config.Bucket),
Key: oss.Ptr(path),
}
preSign, err := c.client.Presign(ctx, request, oss.PresignExpires(time.Minute*5))
if err != nil {
return "", err
}
return preSign.URL, nil
}

View File

@@ -65,4 +65,9 @@ Content-Type: application/json
### precheck
GET {{host}}/v1/admin/uploads/pre-uploaded-check/abc.mp4 HTTP/1.1
Content-Type: application/json
authorization: {{token}}
authorization: {{token}}
### get media url
GET {{host}}/v1/admin/medias/6 HTTP/1.1
Authorization: {{token}}

View File

@@ -11,8 +11,10 @@ export const mediaService = {
return httpClient.post('/admin/medias', mediaInfo);
},
preUploadedCheck(md5, ext) {
return httpClient.get(`/admin/uploads/pre-uploaded-check/${md5}.${ext}`);
preUploadedCheck(md5, ext, mime) {
return httpClient.get(`/admin/uploads/pre-uploaded-check/${md5}.${ext}`, {
params: { mime }
});
},
uploadedSuccess(data) {

View File

@@ -99,8 +99,10 @@ const processNextUpload = async () => {
const md5Hash = await calculateMD5(nextFile.file);
// get file ext
const fileExt = nextFile.file.name.split('.').pop();
console.log(nextFile.file)
// Check if file exists before upload
const checkResult = await mediaService.preUploadedCheck(md5Hash, fileExt);
const checkResult = await mediaService.preUploadedCheck(md5Hash, fileExt, nextFile.file.type);
if (checkResult.data.exists) {
// Skip upload and mark as completed

View File

@@ -30,6 +30,7 @@ const post = reactive({
selectedMedia: [],
medias: [],
status: 0,
head_image: null, // Add head image field
});
// Validation state
@@ -37,7 +38,8 @@ const errors = reactive({
title: '',
introduction: '',
selectedMedia: '',
discount: ''
discount: '',
head_image: '', // Add head image error field
});
// Media selection dialog state
@@ -46,6 +48,7 @@ const selectedMediaItems = ref([]);
const mediaLoading = ref(false);
const mediaGlobalFilter = ref('');
const mediaItems = ref([]);
const isHeadImageSelection = ref(false); // Track if we're selecting head image
// Add pagination state for media dialog
const mediaFirst = ref(0);
@@ -63,8 +66,18 @@ const statusOptions = [
{ label: '草稿', value: 0 }
];
// Open media selection dialog for head image
const openHeadImageDialog = () => {
isHeadImageSelection.value = true;
mediaDialogVisible.value = true;
mediaCurrentPage.value = 1;
mediaFirst.value = 0;
loadMediaItems();
};
// Open media selection dialog
const openMediaDialog = () => {
isHeadImageSelection.value = false;
mediaDialogVisible.value = true;
mediaCurrentPage.value = 1;
mediaFirst.value = 0;
@@ -99,10 +112,16 @@ const onMediaPage = (event) => {
// Confirm media selection
const confirmMediaSelection = () => {
if (selectedMediaItems.value.length) {
if (isHeadImageSelection.value) {
if (selectedMediaItems.value.length) {
post.head_image = selectedMediaItems.value[0];
errors.head_image = '';
}
} else if (selectedMediaItems.value.length) {
post.selectedMedia = [...selectedMediaItems.value];
errors.selectedMedia = '';
}
selectedMediaItems.value = [];
mediaDialogVisible.value = false;
};
@@ -119,6 +138,11 @@ const removeMedia = (media) => {
}
};
// Remove head image
const removeHeadImage = () => {
post.head_image = null;
};
// Save the post
const savePost = async () => {
// Reset errors
@@ -149,13 +173,19 @@ const savePost = async () => {
valid = false;
}
if (!post.head_image) {
errors.head_image = '请选择封面图片';
valid = false;
}
if (!valid) {
toast.add({ severity: 'error', summary: '表单错误', detail: '请检查表单中的错误并修正', life: 3000 });
return;
}
try {
post.medias = post.selectedMedia.map(media => media.id)
post.medias = post.selectedMedia.map(media => media.id);
post.head_image_id = post.head_image.id; // Add head image ID to submission
const resp = await postService.createPost(post);
console.log(resp)
@@ -229,6 +259,34 @@ const formatFileSize = (bytes) => {
<div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Head Image -->
<div class="col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-1">封面图片</label>
<div class="p-4 border border-gray-200 rounded-md">
<div v-if="!post.head_image" class="flex justify-center items-center flex-col space-y-3 py-6">
<i class="pi pi-image text-gray-400 text-5xl!"></i>
<p class="text-gray-500">请选择封面图片</p>
<Button label="选择图片" icon="pi pi-plus" @click="openHeadImageDialog" outlined />
<small v-if="errors.head_image" class="text-red-500">{{ errors.head_image }}</small>
</div>
<div v-else class="relative">
<div class="flex items-center">
<img :src="post.head_image.thumbnailUrl" class="w-32 h-32 object-cover rounded mr-4"
:alt="post.head_image.name">
<div class="flex flex-col">
<span class="font-medium">{{ post.head_image.name }}</span>
<div class="flex gap-2 mt-2">
<Button label="更换图片" icon="pi pi-sync" @click="openHeadImageDialog" outlined
size="small" />
<Button label="移除" icon="pi pi-times" severity="danger" outlined size="small"
@click="removeHeadImage" />
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Title -->
<div class="col-span-2">
<label for="title" class="block text-sm font-medium text-gray-700 mb-1">标题</label>
@@ -316,15 +374,15 @@ const formatFileSize = (bytes) => {
</div>
<!-- Media Selection Dialog -->
<Dialog v-model:visible="mediaDialogVisible" header="选择媒体" :modal="true" :dismissableMask="true" :closable="true"
:style="{ width: '80vw' }" :breakpoints="{ '960px': '90vw' }">
<Dialog v-model:visible="mediaDialogVisible" :header="isHeadImageSelection ? '选择封面图片' : '选择媒体'" :modal="true"
:dismissableMask="true" :closable="true" :style="{ width: '80vw' }" :breakpoints="{ '960px': '90vw' }">
<div class="mb-4">
<InputText v-model="mediaGlobalFilter" placeholder="搜索媒体..." class="w-full" />
</div>
<DataTable v-model:selection="selectedMediaItems" :value="mediaItems" :loading="mediaLoading" dataKey="id"
:paginator="true" v-model:first="mediaFirst" v-model:rows="mediaRows" :totalRecords="mediaTotalRecords"
@page="onMediaPage" selectionMode="multiple"
@page="onMediaPage" :selectionMode="isHeadImageSelection ? 'single' : 'multiple'"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport"
:rows-per-page-options="[10, 25, 50]" currentPageReportTemplate="第 {first} 到 {last} 条,共 {totalRecords} 条"
:lazy="true" :showCurrentPageReport="true">

View File

@@ -6,6 +6,7 @@
"dependencies": {
"@tailwindcss/vite": "^4.1.4",
"@vueuse/core": "^13.1.0",
"axios": "^1.8.4",
"pinia": "^3.0.2",
"tailwindcss": "^4.1.4",
"vue": "^3.5.13",
@@ -187,26 +188,46 @@
"@vueuse/shared": ["@vueuse/shared@13.1.0", "https://registry.npmmirror.com/@vueuse/shared/-/shared-13.1.0.tgz", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-IVS/qRRjhPTZ6C2/AM3jieqXACGwFZwWTdw5sNTSKk2m/ZpkuuN+ri+WCVUP8TqaKwJYt/KuMwmXspMAw8E6ew=="],
"asynckit": ["asynckit@0.4.0", "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"autoprefixer": ["autoprefixer@10.4.21", "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
"axios": ["axios@1.8.4", "https://registry.npmmirror.com/axios/-/axios-1.8.4.tgz", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw=="],
"birpc": ["birpc@0.2.19", "https://registry.npmmirror.com/birpc/-/birpc-0.2.19.tgz", {}, "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ=="],
"browserslist": ["browserslist@4.24.4", "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.4.tgz", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001714", "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001714.tgz", {}, "sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg=="],
"combined-stream": ["combined-stream@1.0.8", "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"copy-anything": ["copy-anything@3.0.5", "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="],
"csstype": ["csstype@3.1.3", "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"delayed-stream": ["delayed-stream@1.0.0", "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"detect-libc": ["detect-libc@2.0.3", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.3.tgz", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"dunder-proto": ["dunder-proto@1.0.1", "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"electron-to-chromium": ["electron-to-chromium@1.5.137", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", {}, "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA=="],
"enhanced-resolve": ["enhanced-resolve@5.18.1", "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
"entities": ["entities@4.5.0", "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"es-define-property": ["es-define-property@1.0.1", "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"esbuild": ["esbuild@0.25.2", "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.2.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.2", "@esbuild/android-arm": "0.25.2", "@esbuild/android-arm64": "0.25.2", "@esbuild/android-x64": "0.25.2", "@esbuild/darwin-arm64": "0.25.2", "@esbuild/darwin-x64": "0.25.2", "@esbuild/freebsd-arm64": "0.25.2", "@esbuild/freebsd-x64": "0.25.2", "@esbuild/linux-arm": "0.25.2", "@esbuild/linux-arm64": "0.25.2", "@esbuild/linux-ia32": "0.25.2", "@esbuild/linux-loong64": "0.25.2", "@esbuild/linux-mips64el": "0.25.2", "@esbuild/linux-ppc64": "0.25.2", "@esbuild/linux-riscv64": "0.25.2", "@esbuild/linux-s390x": "0.25.2", "@esbuild/linux-x64": "0.25.2", "@esbuild/netbsd-arm64": "0.25.2", "@esbuild/netbsd-x64": "0.25.2", "@esbuild/openbsd-arm64": "0.25.2", "@esbuild/openbsd-x64": "0.25.2", "@esbuild/sunos-x64": "0.25.2", "@esbuild/win32-arm64": "0.25.2", "@esbuild/win32-ia32": "0.25.2", "@esbuild/win32-x64": "0.25.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ=="],
"escalade": ["escalade@3.2.0", "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
@@ -215,12 +236,30 @@
"fdir": ["fdir@6.4.3", "https://registry.npmmirror.com/fdir/-/fdir-6.4.3.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
"follow-redirects": ["follow-redirects@1.15.9", "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"form-data": ["form-data@4.0.2", "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
"fraction.js": ["fraction.js@4.3.7", "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
"fsevents": ["fsevents@2.3.3", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"function-bind": ["function-bind@1.1.2", "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"gopd": ["gopd@1.2.0", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"has-symbols": ["has-symbols@1.1.0", "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hookable": ["hookable@5.5.3", "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
"is-what": ["is-what@4.1.16", "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="],
@@ -251,6 +290,12 @@
"magic-string": ["magic-string@0.30.17", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mime-db": ["mime-db@1.52.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"mitt": ["mitt@3.0.1", "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
"nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
@@ -271,6 +316,8 @@
"postcss-value-parser": ["postcss-value-parser@4.2.0", "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"rfdc": ["rfdc@1.4.1", "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
"rollup": ["rollup@4.40.0", "https://registry.npmmirror.com/rollup/-/rollup-4.40.0.tgz", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.0", "@rollup/rollup-android-arm64": "4.40.0", "@rollup/rollup-darwin-arm64": "4.40.0", "@rollup/rollup-darwin-x64": "4.40.0", "@rollup/rollup-freebsd-arm64": "4.40.0", "@rollup/rollup-freebsd-x64": "4.40.0", "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", "@rollup/rollup-linux-arm-musleabihf": "4.40.0", "@rollup/rollup-linux-arm64-gnu": "4.40.0", "@rollup/rollup-linux-arm64-musl": "4.40.0", "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-musl": "4.40.0", "@rollup/rollup-linux-s390x-gnu": "4.40.0", "@rollup/rollup-linux-x64-gnu": "4.40.0", "@rollup/rollup-linux-x64-musl": "4.40.0", "@rollup/rollup-win32-arm64-msvc": "4.40.0", "@rollup/rollup-win32-ia32-msvc": "4.40.0", "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w=="],

View File

@@ -11,6 +11,7 @@
"dependencies": {
"@tailwindcss/vite": "^4.1.4",
"@vueuse/core": "^13.1.0",
"axios": "^1.8.4",
"pinia": "^3.0.2",
"tailwindcss": "^4.1.4",
"vue": "^3.5.13",