diff --git a/admin/src/api/first_visit.ts b/admin/src/api/first_visit.ts
new file mode 100644
index 000000000..9e677b3bf
--- /dev/null
+++ b/admin/src/api/first_visit.ts
@@ -0,0 +1,202 @@
+import request from '@/utils/request'
+
+export interface MyPatientListParams {
+ page_no: number
+ page_size: number
+ keyword?: string
+ status_filter?: '' | 'unconfirmed' | 'booked' | 'completed' | 'missed'
+ start_date?: string
+ end_date?: string
+}
+
+export function myPatientLists(params: MyPatientListParams) {
+ return request.get({ url: '/firstvisit.myPatient/lists', params })
+}
+
+export interface MyPatientOrderListParams {
+ page_no: number
+ page_size: number
+ keyword?: string
+ prescription_audit_status?: '' | number
+ payment_slip_audit_status?: '' | number
+ fulfillment_status?: '' | number
+ start_date?: string
+ end_date?: string
+}
+
+export function myPatientOrderLists(params: MyPatientOrderListParams) {
+ return request.get({ url: '/firstvisit.myPatient/orders', params })
+}
+
+export function myPatientOrderDetail(params: { id: number }) {
+ return request.get({ url: '/firstvisit.myPatient/orderDetail', params })
+}
+
+export function myPatientOrderEdit(params: Record
{{ scopeDescription }}
+当前范围内审核通过的接诊诊单
+与诊单金额指标保持同一审核口径
+部门层级汇总;父级包含其下级数据
+{{ dashboard.target.year }} 年 · 按当前角色、部门及员工筛选范围计算
+从挂号、面诊到接诊成交,统一观察医生经营表现
+按有效诊单金额从高到低
有效诊单数 ÷ 完成面诊数
挂号 → 面诊 → 接诊 → 成交
{{ dashboard.trend.start_date }} 至 {{ dashboard.trend.end_date }}
仅对完成过面诊的医生计算接诊转化预警
+{{ item.suggestion }}
+ {{ formatPercent(item.rate) }} +低于接诊率预警线的医生整行标红,过号与取消分别展示
共 {{ selectedScheduleDay.doctor_count }} 位{{ isOwnershipMode ? '接诊' : '排班' }}医生,点击上方日期可切换
+患者、挂号与诊单信息按当前角色和部门数据范围展示
+{{ row.gender_desc }} · {{ row.age || '—' }}岁 · {{ row.phone_masked || '无手机号' }}
+挂号、诊单与目标数据按当前角色和部门权限实时汇总
+部门汇总可展开查看员工;明日、后日预约始终使用对应自然日
+{{ dashboard.target.year }} 年 · {{ dashboard.target.scope_note }}
++ 尚差 {{ formatMoney(Math.max(0, Number(dashboard.target.target_amount) - Number(dashboard.target.actual_amount))) }} 达成年度目标 +
+当前范围未维护可用目标,实际业绩仍按统一口径正常统计。
+按业务订单创建人排序
按有效挂号数量排序
员工按最深层有效归属部门唯一计入,避免多部门重复累计
授权企业微信后,将多个推广链接按权重、时段和每日上限安全分流
+凭证仅在服务端加密保存
每个方案生成独立 JS
停用或超限自动排除
只记录脱敏访问数据
访客点击时由服务端从当前可用链接中按权重随机选择,页面端不会暴露完整链接池。
+基础脚本只负责捕获点击并请求服务端分流,不携带授权凭证,也不在浏览器中计算随机规则。
+放在页面 </head> 标签之前,只需安装一次。
{{ selectedInstallPool.install_code }}
+ 按钮、图片或文字链接都可以使用同一个数据属性。
{{ selectedInstallPool.trigger_code }}
+ 每次打开都会执行与线上相同的筛选和随机规则,并计入点击数据。
+ {{ scopeDescription }} + + ,当前可见 {{ dashboard.scope.visible_member_count }} 位成员 + +
+{{ appointmentRankingSubtitle }}
+{{ dashboard.rankings.performance.scope_label }}内今日计入业绩的业务订单
+{{ dashboard.trend.date_range.join(' 至 ') }}
+{{ dashboard.target.year_month }},按当前角色数据范围计算
++ 已完成 {{ formatMoney(dashboard.target.completed_amount) }} + + ,目标 {{ formatMoney(dashboard.target.target_amount) }} + +
++ 本人 {{ formatMoney(dashboard.target.personal_amount) }},占当前范围已完成业绩 +
+1&&arguments[1]!==void 0?arguments[1]:{};if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;var i=n.lng||this.resolvedLanguage||this.languages[0],a=!!this.options&&this.options.fallbackLng,u=this.languages[this.languages.length-1];if(i.toLowerCase()==="cimode")return!0;var o=function(l,p){var h=s.services.backendConnector.state["".concat(l,"|").concat(p)];return h===-1||h===2};if(n.precheck){var g=n.precheck(this,o);if(g!==void 0)return g}return!!this.hasResourceBundle(i,t)||!(this.services.backendConnector.backend&&(!this.options.resources||this.options.partialBundledLanguages))||!(!o(i,t)||a&&!o(u,t))}},{key:"loadNamespaces",value:function(t,s){var n=this,i=Ie();return this.options.ns?(typeof t=="string"&&(t=[t]),t.forEach(function(a){n.options.ns.indexOf(a)<0&&n.options.ns.push(a)}),this.loadResources(function(a){i.resolve(),s&&s(a)}),i):(s&&s(),Promise.resolve())}},{key:"loadLanguages",value:function(t,s){var n=Ie();typeof t=="string"&&(t=[t]);var i=this.options.preload||[],a=t.filter(function(u){return i.indexOf(u)<0});return a.length?(this.options.preload=i.concat(a),this.loadResources(function(u){n.resolve(),s&&s(u)}),n):(s&&s(),Promise.resolve())}},{key:"dir",value:function(t){if(t||(t=this.resolvedLanguage||(this.languages&&this.languages.length>0?this.languages[0]:this.language)),!t)return"rtl";var s=this.services&&this.services.languageUtils||new mt(bt());return["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"].indexOf(s.getLanguagePartFromCode(t))>-1||t.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}},{key:"cloneInstance",value:function(){var t=this,s=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:Re,i=N(N(N({},this.options),s),{isClone:!0}),a=new e(i);return s.debug===void 0&&s.prefix===void 0||(a.logger=a.logger.clone(s)),["store","services","language"].forEach(function(u){a[u]=t[u]}),a.services=N({},this.services),a.services.utils={hasLoadedNamespace:a.hasLoadedNamespace.bind(a)},a.translator=new ft(a.services,a.options),a.translator.on("*",function(u){for(var o=arguments.length,g=new Array(o>1?o-1:0),l=1;l