网站首页 > 精选文章 正文
一、工程结构设计(Feature-Based 领域划分)
plaintext
patient-companion-agent/
├── .vscode/ # VS Code配置
│ ├── extensions.json # 推荐插件
│ └── settings.json # 编辑器配置
├── public/ # 静态资源
│ ├── favicon.ico
│ └── index.html
├── src/
│ ├── assets/ # 全局资源(样式、图片)
│ │ ├── styles/ # 全局样式
│ │ │ ├── main.scss
│ │ │ └── variables.scss
│ │ └── images/ # 图片资源
│ ├── common/ # 公共能力(工具、hooks、组件)
│ │ ├── components/ # 公共组件(按钮、输入框等)
│ │ │ ├── BaseButton/
│ │ │ └── BaseInput/
│ │ ├── hooks/ # 公共hooks
│ │ │ ├── useTracker.ts # 埋点统计
│ │ │ ├── useError.ts # 错误处理
│ │ │ └── useStorage.ts # 安全存储
│ │ ├── types/ # 公共类型
│ │ │ └── common.ts
│ │ └── utils/ # 工具函数
│ │ ├── format.ts # 格式化(日期、脱敏)
│ │ ├── security.ts # 安全处理(XSS、加密)
│ │ └── request.ts # API请求封装
│ ├── modules/ # 业务领域模块(DDD划分)
│ │ ├── patient/ # 用户域(患者信息)
│ │ │ ├── components/ # 域内组件
│ │ │ ├── hooks/ # 域内hooks
│ │ │ ├── services/ # 域内服务(业务逻辑+API)
│ │ │ ├── store/ # 域内状态
│ │ │ ├── types/ # 域内类型
│ │ │ └── index.ts # 域导出入口
│ │ ├── companion/ # 陪诊服务域(核心业务)
│ │ │ ├── components/ # 挂号引导、流程查询等组件
│ │ │ ├── hooks/ # 陪诊相关hooks
│ │ │ ├── services/ # 陪诊服务逻辑
│ │ │ ├── store/ # 陪诊状态
│ │ │ ├── types/ # 陪诊类型
│ │ │ └── index.ts
│ │ └── knowledge/ # 知识库域(常见问题)
│ │ ├── components/ # FAQ列表、搜索组件
│ │ ├── services/ # 知识库服务
│ │ ├── store/ # 知识库状态
│ │ ├── types/ # 知识库类型
│ │ └── index.ts
│ ├── router/ # 路由配置
│ │ ├── index.ts
│ │ └── modules/ # 模块路由拆分
│ ├── store/ # Pinia根配置
│ │ └── index.ts
│ ├── App.vue # 根组件
│ ├── main.ts # 入口文件
│ └── env.d.ts # 环境声明
├── .eslintrc.js # ESLint配置
├── .prettierrc # Prettier配置
├── .stylelintrc.js # Stylelint配置
├── commitlint.config.js # Commit规范配置
├── package.json # 依赖配置
├── tsconfig.json # TypeScript配置
└── vite.config.ts # Vite构建配置
二、核心配置文件(确保规范落地)
1. package.json(依赖与脚本)
json
{
"name": "patient-companion-agent",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint:eslint": "eslint . --ext .vue,.js,.ts --fix",
"lint:style": "stylelint \"src/**/*.{vue,scss}\" --fix",
"prepare": "husky install"
},
"dependencies": {
"axios": "^1.6.8",
"crypto-js": "^4.2.0",
"element-plus": "^2.7.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@vitejs/plugin-legacy": "^5.3.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
"husky": "^9.0.11",
"prettier": "^3.2.5",
"sass": "^1.77.0",
"stylelint": "^16.5.0",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-config-standard": "^36.0.0",
"stylelint-scss": "^6.3.0",
"typescript": "^5.4.5",
"vite": "^5.2.11",
"vue-tsc": "^2.0.19"
}
}
2. vite.config.ts(构建与兼容性)
typescript
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import legacy from '@vitejs/plugin-legacy';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
// 基础路径(根据部署环境调整)
base: '/patient-companion/',
// 路径别名
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@assets': resolve(__dirname, 'src/assets'),
'@common': resolve(__dirname, 'src/common'),
'@modules': resolve(__dirname, 'src/modules'),
},
},
// 插件配置
plugins: [
vue(),
// 渐进式增强:兼容低版本浏览器
legacy({
targets: ['defaults', 'not IE 11'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
}),
],
// 服务配置(开发环境)
server: {
port: 3000,
open: true,
cors: true,
},
// 构建优化
build: {
// 资源压缩
minify: 'esbuild',
// 分块策略
rollupOptions: {
output: {
manualChunks: {
// 第三方库拆分
vendor: ['vue', 'vue-router', 'pinia', 'element-plus'],
// 业务模块拆分
patient: ['@modules/patient'],
companion: ['@modules/companion'],
knowledge: ['@modules/knowledge'],
},
},
},
},
});
3. 编码规范配置(.eslintrc.js)
javascript
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier',
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['vue', '@typescript-eslint'],
rules: {
// 基础规范
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// TypeScript规范
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
// Vue规范
'vue/multi-word-component-names': [
'warn',
{ ignores: ['App', 'NotFound'] }, // 允许单字组件名
],
'vue/script-setup-uses-vars': 'error', // 检查script-setup中未使用的变量
},
};
三、核心业务模块实现(DDD 领域建模落地)
1. 公共工具 - 安全处理(src/common/utils/security.ts)
typescript
import CryptoJS from 'crypto-js';
/**
* 安全工具类:处理XSS防护、敏感信息加密/脱敏
*/
export class SecurityUtil {
// 加密密钥(生产环境建议从环境变量获取)
private static readonly SECRET_KEY = import.meta.env.VITE_APP_SECRET_KEY || 'patient-companion-2024';
/**
* XSS防护:过滤HTML标签
* @param html 待过滤的HTML字符串
* @returns 过滤后的纯文本
*/
static filterXSS(html: string): string {
if (!html) return '';
return html
.replace(/<script[^>]*?>[\s\S]*?<\/script>/gi, '')
.replace(/<\/?[^>]*>/gi, '')
.replace(/\s+/g, ' ');
}
/**
* 敏感信息脱敏:就诊卡号(保留前4位和后4位)
* @param cardNo 就诊卡号
* @returns 脱敏后的卡号
*/
static maskMedicalCard(cardNo: string): string {
if (!cardNo) return '';
const reg = /^(\d{4})\d+(\d{4})$/;
return cardNo.replace(reg, '$1****$2');
}
/**
* 本地存储加密:AES加密
* @param data 待加密数据
* @returns 加密后的字符串
*/
static encryptStorage(data: unknown): string {
const jsonStr = JSON.stringify(data);
return CryptoJS.AES.encrypt(jsonStr, this.SECRET_KEY).toString();
}
/**
* 本地存储解密:AES解密
* @param encryptedStr 加密字符串
* @returns 解密后的原始数据
*/
static decryptStorage(encryptedStr: string): unknown {
if (!encryptedStr) return null;
const bytes = CryptoJS.AES.decrypt(encryptedStr, this.SECRET_KEY);
const jsonStr = bytes.toString(CryptoJS.enc.Utf8);
return JSON.parse(jsonStr);
}
}
2. 患者域 - 类型定义(src/modules/patient/types/index.ts)
typescript
/**
* 患者实体(Entity):有唯一标识的业务对象
*/
export interface Patient {
id: string; // 唯一标识(就诊卡号/身份证号)
name: string; // 患者姓名
age: number; // 年龄
gender: 'male' | 'female' | 'other'; // 性别
medicalCardNo: string; // 就诊卡号(敏感信息)
phone: string; // 手机号(敏感信息)
address?: string; // 地址(值对象)
emergencyContact?: EmergencyContact; // 紧急联系人(值对象)
}
/**
* 紧急联系人(Value Object):无唯一标识,不可变
*/
export interface EmergencyContact {
name: string;
phone: string;
relation: string; // 关系(父子、夫妻等)
}
/**
* 患者状态类型
*/
export interface PatientState {
currentPatient: Patient | null; // 当前登录患者
isAuthenticated: boolean; // 是否已认证
loading: boolean; // 加载状态
error: string | null; // 错误信息
}
/**
* 患者登录请求参数
*/
export interface PatientLoginParams {
medicalCardNo: string;
phone: string;
verifyCode: string;
}
3. 患者域 - 状态管理(src/modules/patient/store/index.ts)
typescript
import { defineStore } from 'pinia';
import { persist } from 'pinia-plugin-persistedstate';
import { PatientState, PatientLoginParams, Patient } from '../types';
import { patientService } from '../services';
import { SecurityUtil } from '@common/utils/security';
// 初始状态
const initialState: PatientState = {
currentPatient: null,
isAuthenticated: false,
loading: false,
error: null,
};
/**
* 患者域状态管理:封装患者信息的CRUD与状态维护
*/
export const usePatientStore = defineStore('patient', {
state: (): PatientState => initialState,
// 持久化配置:加密存储敏感信息
persist: {
key: 'patient-companion-patient',
storage: localStorage,
// 自定义序列化(加密)
serialize: (state) => {
if (state.currentPatient) {
// 加密敏感字段
return {
...state,
currentPatient: {
...state.currentPatient,
medicalCardNo: SecurityUtil.encryptStorage(state.currentPatient.medicalCardNo),
phone: SecurityUtil.encryptStorage(state.currentPatient.phone),
},
};
}
return state;
},
// 自定义反序列化(解密)
deserialize: (value) => {
const parsed = JSON.parse(value);
if (parsed.currentPatient) {
// 解密敏感字段
return {
...parsed,
currentPatient: {
...parsed.currentPatient,
medicalCardNo: SecurityUtil.decryptStorage(parsed.currentPatient.medicalCardNo) as string,
phone: SecurityUtil.decryptStorage(parsed.currentPatient.phone) as string,
},
};
}
return parsed;
},
},
actions: {
/**
* 患者登录
* @param params 登录参数
*/
async login(params: PatientLoginParams) {
try {
this.loading = true;
this.error = null;
// 调用服务层接口
const patient = await patientService.login(params);
// 更新状态
this.currentPatient = patient;
this.isAuthenticated = true;
return patient;
} catch (error) {
this.error = error instanceof Error ? error.message : '登录失败,请重试';
throw error;
} finally {
this.loading = false;
}
},
/**
* 患者退出登录
*/
logout() {
this.$reset(); // 重置状态
},
/**
* 更新患者信息
* @param patient 新的患者信息
*/
async updatePatient(patient: Partial<Patient>) {
try {
this.loading = true;
this.error = null;
if (!this.currentPatient) throw new Error('请先登录');
// 调用服务层接口
const updatedPatient = await patientService.update({
...this.currentPatient,
...patient,
});
// 更新状态
this.currentPatient = updatedPatient;
return updatedPatient;
} catch (error) {
this.error = error instanceof Error ? error.message : '更新信息失败';
throw error;
} finally {
this.loading = false;
}
},
},
getters: {
/**
* 脱敏后的就诊卡号
*/
maskedMedicalCard(): string {
return this.currentPatient
? SecurityUtil.maskMedicalCard(this.currentPatient.medicalCardNo)
: '';
},
/**
* 脱敏后的手机号
*/
maskedPhone(): string {
return this.currentPatient
? this.currentPatient.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
: '';
},
},
});
4. 陪诊服务域 - 核心组件(src/modules/companion/components/RegistrationGuide.vue)
vue
<template>
<div class="registration-guide">
<el-page-header content="挂号引导" @back="handleBack" />
<!-- 患者信息校验 -->
<el-card v-if="!patientStore.isAuthenticated" shadow="hover">
<el-alert
title="请先完成患者认证"
type="warning"
show-icon
style="margin-bottom: 16px"
/>
<el-button type="primary" @click="handleGoLogin">前往登录</el-button>
</el-card>
<!-- 挂号引导流程 -->
<el-card v-else shadow="hover" class="mt-4">
<div class="step-container">
<!-- 步骤条 -->
<el-steps :active="currentStep" direction="vertical" class="mb-6">
<el-step title="选择科室" />
<el-step title="选择医生" />
<el-step title="选择就诊时间" />
<el-step title="确认挂号信息" />
</el-steps>
<!-- 步骤内容 -->
<div class="step-content">
<!-- 步骤1:选择科室 -->
<div v-if="currentStep === 0">
<el-select
v-model="selectedDept"
placeholder="请选择就诊科室"
@change="handleDeptChange"
class="w-full mb-4"
>
<el-option
v-for="dept in deptList"
:key="dept.id"
:label="dept.name"
:value="dept"
/>
</el-select>
<el-button
type="primary"
@click="handleNextStep"
:disabled="!selectedDept"
>
下一步
</el-button>
</div>
<!-- 步骤2:选择医生(根据科室筛选) -->
<div v-else-if="currentStep === 1">
<el-table :data="filteredDoctors" border class="mb-4">
<el-table-column label="医生姓名" prop="name" />
<el-table-column label="职称" prop="title" />
<el-table-column label="擅长领域" prop="specialty" />
<el-table-column label="操作">
<template #default="scope">
<el-button
type="text"
@click="handleSelectDoctor(scope.row)"
>
选择
</el-button>
</template>
</el-table-column>
</el-table>
<div class="button-group">
<el-button @click="handlePrevStep">上一步</el-button>
<el-button
type="primary"
@click="handleNextStep"
:disabled="!selectedDoctor"
>
下一步
</el-button>
</div>
</div>
<!-- 步骤3-4:省略(同理实现时间选择、信息确认) -->
</div>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { ElSteps, ElStep, ElCard, ElSelect, ElOption, ElTable, ElTableColumn, ElButton, ElAlert, ElPageHeader } from 'element-plus';
import { usePatientStore } from '@modules/patient/store';
import { companionService } from '@modules/companion/services';
import { Department, Doctor } from '@modules/companion/types';
import { useTracker } from '@common/hooks/useTracker';
// 状态管理
const patientStore = usePatientStore();
const router = useRouter();
const { trackEvent } = useTracker(); // 埋点统计
// 步骤状态
const currentStep = ref(0);
const selectedDept = ref<Department | null>(null);
const selectedDoctor = ref<Doctor | null>(null);
const deptList = ref<Department[]>([]);
const doctorList = ref<Doctor[]>([]);
/**
* 过滤后的医生列表(根据选中的科室)
*/
const filteredDoctors = ref<Doctor[]>([]);
/**
* 页面挂载时加载科室列表
*/
onMounted(async () => {
try {
// 埋点:进入挂号引导页面
trackEvent('companion', 'enter_registration_guide', {
patientId: patientStore.currentPatient?.id,
});
// 加载科室数据
const depts = await companionService.getDepartmentList();
deptList.value = depts;
} catch (error) {
console.error('加载科室列表失败:', error);
}
});
/**
* 处理科室选择变更
*/
const handleDeptChange = async (dept: Department) => {
if (!dept) {
filteredDoctors.value = [];
return;
}
try {
// 根据科室ID加载医生列表
const doctors = await companionService.getDoctorListByDept(dept.id);
doctorList.value = doctors;
filteredDoctors.value = doctors;
} catch (error) {
console.error('加载医生列表失败:', error);
}
};
/**
* 选择医生
*/
const handleSelectDoctor = (doctor: Doctor) => {
selectedDoctor.value = doctor;
// 埋点:选择医生
trackEvent('companion', 'select_doctor', {
doctorId: doctor.id,
deptId: selectedDept.value?.id,
});
};
/**
* 下一步
*/
const handleNextStep = () => {
currentStep.value += 1;
// 埋点:步骤推进
trackEvent('companion', 'next_step', {
currentStep: currentStep.value,
});
};
/**
* 上一步
*/
const handlePrevStep = () => {
currentStep.value -= 1;
};
/**
* 返回上一页
*/
const handleBack = () => {
router.back();
};
/**
* 前往登录
*/
const handleGoLogin = () => {
router.push('/patient/login');
};
</script>
<style scoped lang="scss">
.registration-guide {
padding: 16px;
}
.step-container {
max-width: 800px;
margin: 0 auto;
}
.step-content {
background: #fff;
padding: 24px;
border-radius: 4px;
}
.button-group {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 16px;
}
</style>
5. 知识库域 - 服务层(src/modules/knowledge/services/index.ts)
typescript
import { request } from '@common/utils/request';
import { FAQ, FAQSearchParams } from '../types';
/**
* 知识库服务:处理FAQ查询、搜索等业务逻辑
*/
export class KnowledgeService {
/**
* 获取FAQ列表(分页)
* @param params 分页参数
* @returns FAQ列表与分页信息
*/
static async getFAQList(params: { page: number; size: number }): Promise<{
list: FAQ[];
total: number;
page: number;
size: number;
}> {
const response = await request({
url: '/api/knowledge/faq/list',
method: 'GET',
params,
});
return response.data;
}
/**
* 搜索FAQ(支持关键词匹配)
* @param params 搜索参数
* @returns 匹配的FAQ列表
*/
static async searchFAQ(params: FAQSearchParams): Promise<FAQ[]> {
// 安全处理:过滤XSS
const safeParams = {
...params,
keyword: params.keyword ? SecurityUtil.filterXSS(params.keyword) : '',
};
const response = await request({
url: '/api/knowledge/faq/search',
method: 'GET',
params: safeParams,
});
return response.data.list;
}
/**
* 获取FAQ详情
* @param id FAQ唯一ID
* @returns FAQ详情
*/
static async getFAQDetail(id: string): Promise<FAQ> {
const response = await request({
url: `/api/knowledge/faq/${id}`,
method: 'GET',
});
return response.data;
}
/**
* 记录FAQ点击量(埋点关联)
* @param id FAQ唯一ID
*/
static async recordFAQClick(id: string): Promise<void> {
await request({
url: `/api/knowledge/faq/${id}/click`,
method: 'POST',
});
}
}
// 导出单例(避免重复创建实例)
export const knowledgeService = KnowledgeService;
import { SecurityUtil } from '@common/utils/security';
四、入口与路由配置
1. 主入口(src/main.ts)
typescript
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import { createRouter, createWebHistory } from 'vue-router';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';
import { routes } from './router';
import { useErrorHandler } from '@common/hooks/useError';
import '@assets/styles/main.scss';
// 1. 创建Pinia实例(状态管理)
const pinia = createPinia();
// 安装持久化插件
pinia.use(piniaPluginPersistedstate);
// 2. 创建路由实例
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
});
// 3. 全局错误处理
useErrorHandler();
// 4. 创建Vue应用
const app = createApp(App);
app.use(pinia);
app.use(router);
app.use(ElementPlus);
// 5. 挂载应用
app.mount('#app');
2. 路由配置(src/router/index.ts)
typescript
import { RouteRecordRaw } from 'vue-router';
// 模块路由拆分
import patientRoutes from './modules/patientRoutes';
import companionRoutes from './modules/companionRoutes';
import knowledgeRoutes from './modules/knowledgeRoutes';
/**
* 根路由配置
*/
export const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue'),
meta: {
title: '患者陪诊智能体',
requiresAuth: false, // 是否需要登录
},
},
// 患者域路由
...patientRoutes,
// 陪诊服务域路由
...companionRoutes,
// 知识库域路由
...knowledgeRoutes,
// 404页面
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/NotFound.vue'),
meta: {
title: '页面不存在',
requiresAuth: false,
},
},
];
// 路由守卫:权限控制
router.beforeEach((to, from, next) => {
// 设置页面标题
document.title = to.meta.title as string || '患者陪诊智能体';
// 权限校验:需要登录但未登录的页面,跳转至登录页
const { requiresAuth = false } = to.meta;
const patientStore = usePatientStore();
if (requiresAuth && !patientStore.isAuthenticated) {
next({
path: '/patient/login',
query: { redirect: to.fullPath }, // 登录后跳转回原页面
});
return;
}
next();
});
import { usePatientStore } from '@modules/patient/store';
import { createRouter, createWebHistory } from 'vue-router';
五、工程运行与维护
1. 本地运行步骤
bash
# 1. 安装依赖
npm install
# 2. 启动开发服务(默认3000端口)
npm run dev
# 3. 访问地址
http://localhost:3000/patient-companion/
2. 构建部署
bash
# 1. 构建生产包
npm run build
# 2. 预览生产包
npm run preview
# 3. 部署:将dist目录文件上传至服务器(如Nginx、OSS等)
3. 编码规范执行
bash
# 1. ESLint检查与自动修复
npm run lint:eslint
# 2. Stylelint检查与自动修复
npm run lint:style
# 3. Commit规范:提交代码时自动校验(husky)
git add .
git commit -m "feat: 新增挂号引导功能"
六、符合架构设计要求的关键特性
- DDD 领域建模落地:按患者域、陪诊服务域、知识库域拆分模块,每个域内包含实体、值对象、服务、状态管理,边界清晰。
- 安全要求满足:XSS 防护:SecurityUtil.filterXSS过滤 HTML 标签敏感信息处理:就诊卡号 / 手机号脱敏,本地存储加密API 请求拦截:Axios 拦截器处理请求头与响应错误
- 可观测性:埋点统计:useTracker hook 记录用户操作(如挂号步骤、医生选择)错误处理:useErrorHandler全局捕获错误并上报
- 性能优化:路由按需加载:defineAsyncComponent拆分代码包资源压缩:Vite 构建时自动压缩 JS/CSS/ 图片缓存策略:Pinia 持久化缓存用户信息,localStorage 缓存 FAQ 数据
- 演进性:模块化设计:新增业务域只需在modules目录下添加子模块插件化能力:Pinia 插件、Vue 指令可灵活扩展兼容低版本:@vitejs/plugin-legacy支持 IE11 及以上浏览器
该工程可直接在 VS Code 中打开,安装依赖后即可运行,符合前端架构设计的核心要求,同时满足医院陪诊场景的业务需求。
猜你喜欢
- 2025-09-29 看字节大佬教你2021最新的力扣刷题正确姿势是什么?
- 2025-09-29 我是如何使用 Vim 高效率写 Markdown 的
- 2025-09-29 欢迎新朋友,通义灵码 AI IDE 来了 | 附 QA 答疑
- 2025-09-29 面试官:你为什么用 TS,别人用你就用?
- 2025-09-29 还有人手动画图?一键生成 Draw.io 流程图,3分钟交作业爽炸!
- 2025-09-29 C++ 开发中 compile_commands.json 与 VSCode / clangd 的关系笔记
- 最近发表
- 标签列表
-
- 向日葵无法连接服务器 (32)
- git.exe (33)
- vscode更新 (34)
- dev c (33)
- git ignore命令 (32)
- gitlab提交代码步骤 (37)
- java update (36)
- vue debug (34)
- vue blur (32)
- vscode导入vue项目 (33)
- vue chart (32)
- vue cms (32)
- 大雅数据库 (34)
- 技术迭代 (37)
- 同一局域网 (33)
- github拒绝连接 (33)
- vscode php插件 (32)
- vue注释快捷键 (32)
- linux ssr (33)
- 微端服务器 (35)
- 导航猫 (32)
- 获取当前时间年月日 (33)
- stp软件 (33)
- http下载文件 (33)
- linux bt下载 (33)