fwxq.vue 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019
  1. <script setup lang="ts">
  2. import { computed, nextTick, onMounted, reactive, ref } from 'vue'
  3. import {
  4. ElButton,
  5. ElCard,
  6. ElCol,
  7. ElContainer,
  8. ElDatePicker,
  9. ElDialog,
  10. ElForm,
  11. ElFormItem,
  12. ElIcon,
  13. ElInput,
  14. ElInputNumber,
  15. ElMain,
  16. ElMessage,
  17. ElOption,
  18. ElRow,
  19. ElSelect,
  20. ElStep,
  21. ElSteps,
  22. ElTag,
  23. } from 'element-plus'
  24. import { ArrowLeft, Cpu, Eye, FileText, Home, MapPin, Plus, User, X } from 'lucide-vue-next'
  25. import { useRoute, useRouter } from 'vue-router'
  26. import { clientGet, clientPost } from '@/utils/request.ts'
  27. // 导入docx-preview库
  28. import { renderAsync } from 'docx-preview'
  29. const router = useRouter()
  30. const route = useRoute()
  31. // 响应式数据
  32. const showRentalDialog = ref(false)
  33. const showTerminateDialog = ref(false)
  34. const showDocxPreviewDialog = ref(false) // 新增:DOCX预览对话框
  35. const docxPreviewLoading = ref(false) // 新增:DOCX加载状态
  36. const docxPreviewContainer = ref<HTMLElement>() // 新增:DOCX预览容器引用
  37. const currentStep = ref(0)
  38. const submitting = ref(false)
  39. const terminating = ref(false)
  40. const loading = ref(true)
  41. const MINIO_URL = import.meta.env.VITE_MINIO_BASE_URL
  42. const propertyInfo = ref({
  43. id: '',
  44. buildingNumber: '',
  45. priceRange: '',
  46. address: '',
  47. area: '',
  48. status: '', // 未租 或 已租
  49. floor: '',
  50. facilities: '',
  51. assetType: '', //公租房|厂房|创新创业基地
  52. })
  53. const basicInfo = ref({
  54. buildYear: '',
  55. orientation: '',
  56. houseType: '',
  57. })
  58. const assetFilter = reactive({
  59. category: '',
  60. })
  61. const assets = ref<DeviceInfo[]>([])
  62. const filteredAssets = computed(() => {
  63. return assets.value.filter((asset) => {
  64. const categoryMatch = !assetFilter.category || asset.deviceType === assetFilter.category
  65. return categoryMatch
  66. })
  67. })
  68. const tenantInfo = ref<TenantInfo | null>(null)
  69. const contractInfo = ref<ContractInfo | null>(null)
  70. interface ContractA {
  71. tenantName: string
  72. landlordName: string
  73. tenantTimeStart: string
  74. tenantTime: string
  75. tenantTimeEnd: string
  76. tenantRent: string
  77. tenantDeposit: string
  78. tenantDepositAmount: string
  79. }
  80. interface ContractB {
  81. purpose: string
  82. landlordName: string
  83. landlordUniCode: string
  84. landlordLegalRepresentative: string
  85. landlordDuty: string
  86. tenantName: string
  87. tenantUniCode: string
  88. tenantLegalRepresentative: string
  89. tenantDuty: string
  90. who: string
  91. whatTime: string
  92. title: string
  93. supportingRoomArea: string
  94. officeArea: string
  95. canteenArea: string
  96. totalLeasedArea: string
  97. dormTotalRooms: string
  98. dormSmallRooms: string
  99. dormLargeRooms: string
  100. deliveryDate: string
  101. productionProject: string
  102. leaseTermYears: string
  103. leaseStartDate: string
  104. leaseEndDate: string
  105. renewNoticeMonths: string
  106. factorySpecificFloor1: string
  107. rentPerSqmFloor1: string
  108. factorySpecificFloor2: string
  109. rentPerSqmFloor2: string
  110. factoryAnnualRent: string
  111. supportingRoomRentPerSqm: string
  112. supportingRoomAnnualRent: string
  113. dormSmallRoomRent: string
  114. dormLargeRoomRent: string
  115. dormAnnualRent: string
  116. totalAnnualRent: string
  117. propertyFeePerSqm: string
  118. factorySupportingPropertyFee: string
  119. dormSmallRoomPropertyFee: string
  120. dormLargeRoomPropertyFee: string
  121. dormAnnualPropertyFee: string
  122. totalAnnualPropertyFee: string
  123. annualTotalRentAndPropertyFee: string
  124. depositAmount: string
  125. }
  126. interface ContractC {
  127. lessorName: string
  128. lessorLegalRep: string
  129. lessorUniCode: string
  130. lesseeName: string
  131. lesseeLegalRep: string
  132. lesseeIdCode: string
  133. propertyLocation: string
  134. rentableArea: string
  135. usableArea: string
  136. sharedArea: string
  137. propertyUseNature: string
  138. storeName: string
  139. brand: string
  140. businessScope: string
  141. leaseTermMonths: string
  142. leaseStartDate: string
  143. leaseEndDate: string
  144. renovationStartDate: string
  145. renovationEndDate: string
  146. officialLeaseStartDate: string
  147. annualRentLower: string
  148. annualPropertyFeeLower: string
  149. depositAmount: string
  150. }
  151. const rentalForm = reactive<{
  152. houseId: string
  153. contractData: ContractA | ContractB | ContractC | null
  154. contractDataA: ContractA
  155. contractDataB: ContractB
  156. contractDataC: ContractC
  157. rentalInfo: {
  158. rentalName: string
  159. rentalPhone: string
  160. rentalIdCard: string
  161. rentalTimeStart: string
  162. rentalTime: string
  163. rentalRect: string
  164. }
  165. }>({
  166. houseId: route.query.houseId as string,
  167. contractData: null,
  168. rentalInfo: {
  169. rentalName: '',
  170. rentalPhone: '',
  171. rentalIdCard: '',
  172. rentalTimeStart: '',
  173. rentalTime: '',
  174. rentalRect: '',
  175. },
  176. contractDataA: {
  177. landlordName: '',
  178. tenantDeposit: '',
  179. tenantDepositAmount: '',
  180. tenantName: '',
  181. tenantRent: '',
  182. tenantTime: '',
  183. tenantTimeEnd: '',
  184. tenantTimeStart: '',
  185. },
  186. contractDataB: {
  187. annualTotalRentAndPropertyFee: '',
  188. canteenArea: '',
  189. deliveryDate: '',
  190. depositAmount: '',
  191. dormAnnualPropertyFee: '',
  192. dormAnnualRent: '',
  193. dormLargeRoomPropertyFee: '',
  194. dormLargeRoomRent: '',
  195. dormLargeRooms: '',
  196. dormSmallRoomPropertyFee: '',
  197. dormSmallRoomRent: '',
  198. dormSmallRooms: '',
  199. dormTotalRooms: '',
  200. factoryAnnualRent: '',
  201. factorySpecificFloor1: '',
  202. factorySpecificFloor2: '',
  203. factorySupportingPropertyFee: '',
  204. landlordDuty: '',
  205. landlordLegalRepresentative: '',
  206. landlordName: '',
  207. landlordUniCode: '',
  208. leaseEndDate: '',
  209. leaseStartDate: '',
  210. leaseTermYears: '',
  211. officeArea: '',
  212. productionProject: '',
  213. propertyFeePerSqm: '',
  214. purpose: '',
  215. renewNoticeMonths: '',
  216. rentPerSqmFloor1: '',
  217. rentPerSqmFloor2: '',
  218. supportingRoomAnnualRent: '',
  219. supportingRoomArea: '',
  220. supportingRoomRentPerSqm: '',
  221. tenantDuty: '',
  222. tenantLegalRepresentative: '',
  223. tenantName: '',
  224. tenantUniCode: '',
  225. title: '',
  226. totalAnnualPropertyFee: '',
  227. totalAnnualRent: '',
  228. totalLeasedArea: '',
  229. whatTime: '',
  230. who: '',
  231. },
  232. contractDataC: {
  233. annualPropertyFeeLower: '',
  234. annualRentLower: '',
  235. brand: '',
  236. businessScope: '',
  237. depositAmount: '',
  238. leaseEndDate: '',
  239. leaseStartDate: '',
  240. leaseTermMonths: '',
  241. lesseeIdCode: '',
  242. lesseeLegalRep: '',
  243. lesseeName: '',
  244. lessorLegalRep: '',
  245. lessorName: '',
  246. lessorUniCode: '',
  247. officialLeaseStartDate: '',
  248. propertyLocation: '',
  249. propertyUseNature: '',
  250. renovationEndDate: '',
  251. renovationStartDate: '',
  252. rentableArea: '',
  253. sharedArea: '',
  254. storeName: '',
  255. usableArea: '',
  256. },
  257. })
  258. const rentalRules = {
  259. // 租户信息验证规则
  260. 'rentalInfo.rentalName': [{ required: true, message: '请输入租户姓名', trigger: 'blur' }],
  261. 'rentalInfo.rentalPhone': [{ required: true, message: '请输入租户电话', trigger: 'blur' }],
  262. 'rentalInfo.rentalIdCard': [{ required: true, message: '请输入租户身份证号', trigger: 'blur' }],
  263. 'rentalInfo.rentalTimeStart': [
  264. { required: true, message: '请输入租期开始时间', trigger: 'blur' },
  265. ],
  266. 'rentalInfo.rentalTime': [{ required: true, message: '请输入租期', trigger: 'blur' }],
  267. 'rentalInfo.rentalRect': [{ required: true, message: '请输入租金', trigger: 'blur' }],
  268. // 公租房合同A验证规则
  269. 'contractDataA.tenantName': [{ required: true, message: '请输入租方', trigger: 'blur' }],
  270. 'contractDataA.landlordName': [{ required: true, message: '请输入房东', trigger: 'blur' }],
  271. 'contractDataA.tenantTimeStart': [
  272. { required: true, message: '请输入租期开始时间', trigger: 'blur' },
  273. ],
  274. 'contractDataA.tenantTime': [{ required: true, message: '请输入租期', trigger: 'blur' }],
  275. 'contractDataA.tenantTimeEnd': [
  276. { required: true, message: '请输入租期结束时间', trigger: 'blur' },
  277. ],
  278. 'contractDataA.tenantRent': [{ required: true, message: '请输入每套租金', trigger: 'blur' }],
  279. 'contractDataA.tenantDeposit': [{ required: true, message: '请输入每套物业费', trigger: 'blur' }],
  280. 'contractDataA.tenantDepositAmount': [{ required: true, message: '请输入押金', trigger: 'blur' }],
  281. // 厂房合同B验证规则 - 所有字段必填
  282. 'contractDataB.purpose': [{ required: true, message: '请输入用途', trigger: 'blur' }],
  283. 'contractDataB.landlordName': [{ required: true, message: '请输入房东', trigger: 'blur' }],
  284. 'contractDataB.landlordUniCode': [
  285. { required: true, message: '请输入法人统一社会信用代码', trigger: 'blur' },
  286. ],
  287. 'contractDataB.landlordLegalRepresentative': [
  288. { required: true, message: '请输入法定代表人', trigger: 'blur' },
  289. ],
  290. 'contractDataB.landlordDuty': [{ required: true, message: '请输入房东职务', trigger: 'blur' }],
  291. 'contractDataB.tenantName': [{ required: true, message: '请输入租方', trigger: 'blur' }],
  292. 'contractDataB.tenantUniCode': [
  293. { required: true, message: '请输入租方统一社会信用代码', trigger: 'blur' },
  294. ],
  295. 'contractDataB.tenantLegalRepresentative': [
  296. { required: true, message: '请输入法定代表人', trigger: 'blur' },
  297. ],
  298. 'contractDataB.tenantDuty': [{ required: true, message: '请输入租方职务', trigger: 'blur' }],
  299. 'contractDataB.who': [{ required: true, message: '请输入谁', trigger: 'blur' }],
  300. 'contractDataB.whatTime': [{ required: true, message: '请输入何时', trigger: 'blur' }],
  301. 'contractDataB.title': [{ required: true, message: '请输入标题', trigger: 'blur' }],
  302. 'contractDataB.supportingRoomArea': [
  303. { required: true, message: '请输入配套用房面积(㎡)', trigger: 'blur' },
  304. ],
  305. 'contractDataB.officeArea': [
  306. { required: true, message: '请输入办公室面积(㎡)', trigger: 'blur' },
  307. ],
  308. 'contractDataB.canteenArea': [
  309. { required: true, message: '请输入食堂面积(㎡)', trigger: 'blur' },
  310. ],
  311. 'contractDataB.totalLeasedArea': [
  312. { required: true, message: '请输入总面积(㎡)', trigger: 'blur' },
  313. ],
  314. 'contractDataB.dormTotalRooms': [
  315. { required: true, message: '请输入宿舍楼总间数', trigger: 'blur' },
  316. ],
  317. 'contractDataB.dormSmallRooms': [
  318. { required: true, message: '请输入宿舍小间数', trigger: 'blur' },
  319. ],
  320. 'contractDataB.dormLargeRooms': [
  321. { required: true, message: '请输入宿舍大间数', trigger: 'blur' },
  322. ],
  323. 'contractDataB.deliveryDate': [
  324. { required: true, message: '请输入标的交付日期', trigger: 'blur' },
  325. ],
  326. 'contractDataB.productionProject': [
  327. { required: true, message: '请输入生产项目名称', trigger: 'blur' },
  328. ],
  329. 'contractDataB.leaseTermYears': [
  330. { required: true, message: '请输入租赁期限(年)', trigger: 'blur' },
  331. ],
  332. 'contractDataB.leaseStartDate': [
  333. { required: true, message: '请输入租赁起始日', trigger: 'blur' },
  334. ],
  335. 'contractDataB.leaseEndDate': [{ required: true, message: '请输入租赁终止日', trigger: 'blur' }],
  336. 'contractDataB.renewNoticeMonths': [
  337. { required: true, message: '请输入续租提前通知月数', trigger: 'blur' },
  338. ],
  339. 'contractDataB.factorySpecificFloor1': [
  340. { required: true, message: '请输入标准化厂房某楼层', trigger: 'blur' },
  341. ],
  342. 'contractDataB.rentPerSqmFloor1': [
  343. { required: true, message: '请输入该楼层租金(元/㎡/月)', trigger: 'blur' },
  344. ],
  345. 'contractDataB.factorySpecificFloor2': [
  346. { required: true, message: '请输入标准化厂房另一楼层', trigger: 'blur' },
  347. ],
  348. 'contractDataB.rentPerSqmFloor2': [
  349. { required: true, message: '请输入该楼层租金(元/㎡/月)', trigger: 'blur' },
  350. ],
  351. 'contractDataB.factoryAnnualRent': [
  352. { required: true, message: '请输入厂房年租金(元)', trigger: 'blur' },
  353. ],
  354. 'contractDataB.supportingRoomRentPerSqm': [
  355. { required: true, message: '请输入配套用房租金(元/㎡/月)', trigger: 'blur' },
  356. ],
  357. 'contractDataB.supportingRoomAnnualRent': [
  358. { required: true, message: '请输入配套用房年租金(元)', trigger: 'blur' },
  359. ],
  360. 'contractDataB.dormSmallRoomRent': [
  361. { required: true, message: '请输入宿舍楼普通单间月租金(元)', trigger: 'blur' },
  362. ],
  363. 'contractDataB.dormLargeRoomRent': [
  364. { required: true, message: '请输入宿舍楼大单间月租金(元)', trigger: 'blur' },
  365. ],
  366. 'contractDataB.dormAnnualRent': [
  367. { required: true, message: '请输入宿舍楼年租金(元)', trigger: 'blur' },
  368. ],
  369. 'contractDataB.totalAnnualRent': [
  370. { required: true, message: '请输入总租金(元)', trigger: 'blur' },
  371. ],
  372. 'contractDataB.propertyFeePerSqm': [
  373. { required: true, message: '请输入标准化厂房和配套用房物业费(元/㎡/月)', trigger: 'blur' },
  374. ],
  375. 'contractDataB.factorySupportingPropertyFee': [
  376. { required: true, message: '请输入标准化厂房和配套用房年物业费(元)', trigger: 'blur' },
  377. ],
  378. 'contractDataB.dormSmallRoomPropertyFee': [
  379. { required: true, message: '请输入宿舍楼普通单间月物业费(元)', trigger: 'blur' },
  380. ],
  381. 'contractDataB.dormLargeRoomPropertyFee': [
  382. { required: true, message: '请输入宿舍楼大单间月物业费(元)', trigger: 'blur' },
  383. ],
  384. 'contractDataB.dormAnnualPropertyFee': [
  385. { required: true, message: '请输入宿舍楼年物业费(元)', trigger: 'blur' },
  386. ],
  387. 'contractDataB.totalAnnualPropertyFee': [
  388. { required: true, message: '请输入年物业管理费(元)', trigger: 'blur' },
  389. ],
  390. 'contractDataB.annualTotalRentAndPropertyFee': [
  391. { required: true, message: '请输入年租金和物业费用总计(元)', trigger: 'blur' },
  392. ],
  393. 'contractDataB.depositAmount': [
  394. { required: true, message: '请输入租赁押金金额(元)', trigger: 'blur' },
  395. ],
  396. // 创新创业基地合同C验证规则 - 所有字段必填
  397. 'contractDataC.lessorName': [
  398. { required: true, message: '请输入出租方(甲方)名称', trigger: 'blur' },
  399. ],
  400. 'contractDataC.lessorLegalRep': [
  401. { required: true, message: '请输入出租方法定代表人', trigger: 'blur' },
  402. ],
  403. 'contractDataC.lessorUniCode': [
  404. { required: true, message: '请输入出租方统一社会信用代码', trigger: 'blur' },
  405. ],
  406. 'contractDataC.lesseeName': [
  407. { required: true, message: '请输入承租方(乙方)名称', trigger: 'blur' },
  408. ],
  409. 'contractDataC.lesseeLegalRep': [
  410. { required: true, message: '请输入承租方法定代表人', trigger: 'blur' },
  411. ],
  412. 'contractDataC.lesseeIdCode': [
  413. { required: true, message: '请输入承租方统一社会信用代码/身份证号', trigger: 'blur' },
  414. ],
  415. 'contractDataC.propertyLocation': [
  416. { required: true, message: '请输入标的物坐落', trigger: 'blur' },
  417. ],
  418. 'contractDataC.rentableArea': [
  419. { required: true, message: '请输入计租面积(平方米)', trigger: 'blur' },
  420. ],
  421. 'contractDataC.usableArea': [
  422. { required: true, message: '请输入使用面积(平方米)', trigger: 'blur' },
  423. ],
  424. 'contractDataC.sharedArea': [
  425. { required: true, message: '请输入公摊面积(平方米)', trigger: 'blur' },
  426. ],
  427. 'contractDataC.propertyUseNature': [
  428. { required: true, message: '请输入标的物使用性质', trigger: 'blur' },
  429. ],
  430. 'contractDataC.storeName': [{ required: true, message: '请输入店铺名称', trigger: 'blur' }],
  431. 'contractDataC.brand': [{ required: true, message: '请输入商品/服务品牌', trigger: 'blur' }],
  432. 'contractDataC.businessScope': [{ required: true, message: '请输入经营范围', trigger: 'blur' }],
  433. 'contractDataC.leaseTermMonths': [
  434. { required: true, message: '请输入租赁期限(个月)', trigger: 'blur' },
  435. ],
  436. 'contractDataC.leaseStartDate': [
  437. { required: true, message: '请输入租赁起始日', trigger: 'blur' },
  438. ],
  439. 'contractDataC.leaseEndDate': [{ required: true, message: '请输入租赁终止日', trigger: 'blur' }],
  440. 'contractDataC.renovationStartDate': [
  441. { required: true, message: '请输入装修期起始日', trigger: 'blur' },
  442. ],
  443. 'contractDataC.renovationEndDate': [
  444. { required: true, message: '请输入装修期终止日', trigger: 'blur' },
  445. ],
  446. 'contractDataC.officialLeaseStartDate': [
  447. { required: true, message: '请输入正式起租时间', trigger: 'blur' },
  448. ],
  449. 'contractDataC.annualRentLower': [
  450. { required: true, message: '请输入年租金(小写)', trigger: 'blur' },
  451. ],
  452. 'contractDataC.annualPropertyFeeLower': [
  453. { required: true, message: '请输入年物业服务费(小写)', trigger: 'blur' },
  454. ],
  455. 'contractDataC.depositAmount': [
  456. { required: true, message: '请输入租赁保证金金额', trigger: 'blur' },
  457. ],
  458. }
  459. const terminateForm = reactive({
  460. terminateDate: '',
  461. reason: '',
  462. depositHandling: '全额退还',
  463. deductAmount: 0,
  464. })
  465. const rentalFormRef = ref()
  466. interface BaseResponse {
  467. code: number
  468. msg: string
  469. }
  470. interface HouseInfoVo {
  471. houseName: string | null
  472. address: string | null
  473. status: string | null
  474. rentRange: number | null
  475. tenantInfo: TenantInfo | null
  476. houseInfo: HouseInfo | null
  477. deviceInfo: DeviceInfo[] | null
  478. contractInfo: ContractInfo | null
  479. assetType: string | null
  480. }
  481. interface HouseInfoResponse extends BaseResponse {
  482. data: HouseInfoVo
  483. }
  484. interface TenantInfo {
  485. tenantName: string | null
  486. tenantNumber: string | null
  487. tenantIdCard: string | null
  488. tenantInDate: string | null
  489. tenantTime: string | null
  490. tenantRent: string | null
  491. }
  492. interface HouseInfo {
  493. area: string | null
  494. floor: string | null
  495. introduce: string | null
  496. houseType: string | null
  497. }
  498. interface DeviceInfo {
  499. deviceName: string | null
  500. deviceType: string | null
  501. deviceBrand: string | null
  502. deviceNumber: string | null
  503. devicePrice: string | null
  504. }
  505. interface ContractInfo {
  506. contractNumber: string | null
  507. contractDate: string | null
  508. contractTime: string | null
  509. contractExpirationDate: string | null
  510. contractDeposit: string | null
  511. contractStatus: string | null
  512. contractOriginalUrl: string | null
  513. }
  514. const deviceTypes = computed(() => {
  515. const types = new Set(assets.value.map((asset) => asset.deviceType).filter(Boolean))
  516. return Array.from(types)
  517. })
  518. // const openARViewing = () => {
  519. // ElMessage.info('正在启动AR看房功能...')
  520. // }
  521. const filterAssets = () => {
  522. // 筛选逻辑已通过计算属性实现
  523. }
  524. // 新增:预览DOCX文件的函数
  525. const previewDocx = async (url: string) => {
  526. url = MINIO_URL + url
  527. if (!url) {
  528. ElMessage.error('合同文件URL不存在')
  529. return
  530. }
  531. showDocxPreviewDialog.value = true
  532. docxPreviewLoading.value = true
  533. try {
  534. // 获取DOCX文件
  535. const response = await fetch(url)
  536. if (!response.ok) {
  537. throw new Error('无法获取合同文件')
  538. }
  539. const arrayBuffer = await response.arrayBuffer()
  540. // 等待DOM更新
  541. await nextTick()
  542. if (docxPreviewContainer.value) {
  543. // 清空容器
  544. docxPreviewContainer.value.innerHTML = ''
  545. // 渲染DOCX内容
  546. await renderAsync(arrayBuffer, docxPreviewContainer.value, undefined, {
  547. className: 'docx-wrapper',
  548. inWrapper: true,
  549. ignoreWidth: false,
  550. ignoreHeight: false,
  551. ignoreFonts: false,
  552. breakPages: true,
  553. ignoreLastRenderedPageBreak: true,
  554. experimental: false,
  555. trimXmlDeclaration: true,
  556. useBase64URL: false,
  557. renderHeaders: true,
  558. renderFooters: true,
  559. renderFootnotes: true,
  560. renderEndnotes: true,
  561. debug: false,
  562. })
  563. }
  564. } catch (error) {
  565. console.error('预览DOCX文件失败:', error)
  566. ElMessage.error('预览合同文件失败,请稍后重试')
  567. showDocxPreviewDialog.value = false
  568. } finally {
  569. docxPreviewLoading.value = false
  570. }
  571. }
  572. // 新增:关闭DOCX预览对话框
  573. const closeDocxPreview = () => {
  574. showDocxPreviewDialog.value = false
  575. if (docxPreviewContainer.value) {
  576. docxPreviewContainer.value.innerHTML = ''
  577. }
  578. }
  579. const getValidationFields = () => {
  580. const assetType = propertyInfo.value.assetType
  581. if (assetType === '公租房') {
  582. return [
  583. 'contractDataA.tenantName',
  584. 'contractDataA.landlordName',
  585. 'contractDataA.tenantTimeStart',
  586. 'contractDataA.tenantTime',
  587. 'contractDataA.tenantTimeEnd',
  588. 'contractDataA.tenantRent',
  589. 'contractDataA.tenantDeposit',
  590. 'contractDataA.tenantDepositAmount',
  591. ]
  592. } else if (assetType === '厂房') {
  593. return [
  594. 'contractDataB.purpose',
  595. 'contractDataB.landlordName',
  596. 'contractDataB.landlordUniCode',
  597. 'contractDataB.landlordLegalRepresentative',
  598. 'contractDataB.landlordDuty',
  599. 'contractDataB.tenantName',
  600. 'contractDataB.tenantUniCode',
  601. 'contractDataB.tenantLegalRepresentative',
  602. 'contractDataB.tenantDuty',
  603. 'contractDataB.who',
  604. 'contractDataB.whatTime',
  605. 'contractDataB.title',
  606. 'contractDataB.supportingRoomArea',
  607. 'contractDataB.officeArea',
  608. 'contractDataB.canteenArea',
  609. 'contractDataB.totalLeasedArea',
  610. 'contractDataB.dormTotalRooms',
  611. 'contractDataB.dormSmallRooms',
  612. 'contractDataB.dormLargeRooms',
  613. 'contractDataB.deliveryDate',
  614. 'contractDataB.productionProject',
  615. 'contractDataB.leaseTermYears',
  616. 'contractDataB.leaseStartDate',
  617. 'contractDataB.leaseEndDate',
  618. 'contractDataB.renewNoticeMonths',
  619. 'contractDataB.factorySpecificFloor1',
  620. 'contractDataB.rentPerSqmFloor1',
  621. 'contractDataB.factorySpecificFloor2',
  622. 'contractDataB.rentPerSqmFloor2',
  623. 'contractDataB.factoryAnnualRent',
  624. 'contractDataB.supportingRoomRentPerSqm',
  625. 'contractDataB.supportingRoomAnnualRent',
  626. 'contractDataB.dormSmallRoomRent',
  627. 'contractDataB.dormLargeRoomRent',
  628. 'contractDataB.dormAnnualRent',
  629. 'contractDataB.totalAnnualRent',
  630. 'contractDataB.propertyFeePerSqm',
  631. 'contractDataB.factorySupportingPropertyFee',
  632. 'contractDataB.dormSmallRoomPropertyFee',
  633. 'contractDataB.dormLargeRoomPropertyFee',
  634. 'contractDataB.dormAnnualPropertyFee',
  635. 'contractDataB.totalAnnualPropertyFee',
  636. 'contractDataB.annualTotalRentAndPropertyFee',
  637. 'contractDataB.depositAmount',
  638. ]
  639. } else if (assetType === '创新创业基地') {
  640. return [
  641. 'contractDataC.lessorName',
  642. 'contractDataC.lessorLegalRep',
  643. 'contractDataC.lessorUniCode',
  644. 'contractDataC.lesseeName',
  645. 'contractDataC.lesseeLegalRep',
  646. 'contractDataC.lesseeIdCode',
  647. 'contractDataC.propertyLocation',
  648. 'contractDataC.rentableArea',
  649. 'contractDataC.usableArea',
  650. 'contractDataC.sharedArea',
  651. 'contractDataC.propertyUseNature',
  652. 'contractDataC.storeName',
  653. 'contractDataC.brand',
  654. 'contractDataC.businessScope',
  655. 'contractDataC.leaseTermMonths',
  656. 'contractDataC.leaseStartDate',
  657. 'contractDataC.leaseEndDate',
  658. 'contractDataC.renovationStartDate',
  659. 'contractDataC.renovationEndDate',
  660. 'contractDataC.officialLeaseStartDate',
  661. 'contractDataC.annualRentLower',
  662. 'contractDataC.annualPropertyFeeLower',
  663. 'contractDataC.depositAmount',
  664. ]
  665. }
  666. return []
  667. }
  668. const nextStep = async () => {
  669. if (currentStep.value === 0) {
  670. try {
  671. await rentalFormRef.value.validateField([
  672. 'rentalInfo.rentalName',
  673. 'rentalInfo.rentalPhone',
  674. 'rentalInfo.rentalIdCard',
  675. 'rentalInfo.rentalTimeStart',
  676. 'rentalInfo.rentalTime',
  677. 'rentalInfo.rentalRect',
  678. ])
  679. currentStep.value++
  680. } catch (error) {
  681. console.log('第一步验证失败:', error)
  682. }
  683. } else if (currentStep.value === 1) {
  684. try {
  685. const fieldsToValidate = getValidationFields()
  686. await rentalFormRef.value.validateField(fieldsToValidate)
  687. currentStep.value++
  688. } catch (error) {
  689. console.log('第二步验证失败:', error)
  690. }
  691. }
  692. }
  693. const handleCloseRentalDialog = () => {
  694. showRentalDialog.value = false
  695. currentStep.value = 0
  696. Object.assign(rentalForm.rentalInfo, {
  697. rentalName: '',
  698. rentalPhone: '',
  699. rentalIdCard: '',
  700. rentalTimeStart: '',
  701. rentalTime: '',
  702. rentalRect: '',
  703. })
  704. Object.assign(rentalForm.contractDataA, {
  705. landlordName: '',
  706. tenantDeposit: '',
  707. tenantDepositAmount: '',
  708. tenantName: '',
  709. tenantRent: '',
  710. tenantTime: '',
  711. tenantTimeEnd: '',
  712. tenantTimeStart: '',
  713. })
  714. // 重置contractDataB的所有字段
  715. Object.assign(rentalForm.contractDataB, {
  716. annualTotalRentAndPropertyFee: '',
  717. canteenArea: '',
  718. deliveryDate: '',
  719. depositAmount: '',
  720. dormAnnualPropertyFee: '',
  721. dormAnnualRent: '',
  722. dormLargeRoomPropertyFee: '',
  723. dormLargeRoomRent: '',
  724. dormLargeRooms: '',
  725. dormSmallRoomPropertyFee: '',
  726. dormSmallRoomRent: '',
  727. dormSmallRooms: '',
  728. dormTotalRooms: '',
  729. factoryAnnualRent: '',
  730. factorySpecificFloor1: '',
  731. factorySpecificFloor2: '',
  732. factorySupportingPropertyFee: '',
  733. landlordDuty: '',
  734. landlordLegalRepresentative: '',
  735. landlordName: '',
  736. landlordUniCode: '',
  737. leaseEndDate: '',
  738. leaseStartDate: '',
  739. leaseTermYears: '',
  740. officeArea: '',
  741. productionProject: '',
  742. propertyFeePerSqm: '',
  743. purpose: '',
  744. renewNoticeMonths: '',
  745. rentPerSqmFloor1: '',
  746. rentPerSqmFloor2: '',
  747. supportingRoomAnnualRent: '',
  748. supportingRoomArea: '',
  749. supportingRoomRentPerSqm: '',
  750. tenantDuty: '',
  751. tenantLegalRepresentative: '',
  752. tenantName: '',
  753. tenantUniCode: '',
  754. title: '',
  755. totalAnnualPropertyFee: '',
  756. totalAnnualRent: '',
  757. totalLeasedArea: '',
  758. whatTime: '',
  759. who: '',
  760. })
  761. // 重置contractDataC的所有字段
  762. Object.assign(rentalForm.contractDataC, {
  763. annualPropertyFeeLower: '',
  764. annualRentLower: '',
  765. brand: '',
  766. businessScope: '',
  767. depositAmount: '',
  768. leaseEndDate: '',
  769. leaseStartDate: '',
  770. leaseTermMonths: '',
  771. lesseeIdCode: '',
  772. lesseeLegalRep: '',
  773. lesseeName: '',
  774. lessorLegalRep: '',
  775. lessorName: '',
  776. lessorUniCode: '',
  777. officialLeaseStartDate: '',
  778. propertyLocation: '',
  779. propertyUseNature: '',
  780. renovationEndDate: '',
  781. renovationStartDate: '',
  782. rentableArea: '',
  783. sharedArea: '',
  784. storeName: '',
  785. usableArea: '',
  786. })
  787. }
  788. const submitRental = async () => {
  789. submitting.value = true
  790. try {
  791. if (propertyInfo.value.assetType === '公租房') {
  792. rentalForm.contractData = rentalForm.contractDataA
  793. } else if (propertyInfo.value.assetType === '厂房') {
  794. rentalForm.contractData = rentalForm.contractDataB
  795. } else if (propertyInfo.value.assetType === '创新创业基地') {
  796. rentalForm.contractData = rentalForm.contractDataC
  797. }
  798. const res = await clientPost<typeof rentalForm, BaseResponse>(
  799. '/acontractInfo/signContract',
  800. rentalForm,
  801. )
  802. if (res.code !== 200) {
  803. ElMessage.error(res.msg)
  804. return
  805. }
  806. ElMessage.success(res.msg)
  807. handleCloseRentalDialog()
  808. await getData()
  809. // 新增:签订合同成功后自动预览DOCX
  810. if (contractInfo.value?.contractOriginalUrl) {
  811. setTimeout(() => {
  812. previewDocx(contractInfo.value!.contractOriginalUrl!)
  813. }, 500) // 延迟500ms确保数据更新完成
  814. }
  815. } catch (error) {
  816. console.error(error)
  817. ElMessage.error('出租失败,请重试')
  818. } finally {
  819. submitting.value = false
  820. }
  821. }
  822. const confirmTerminate = async () => {
  823. terminating.value = true
  824. try {
  825. const res = await clientGet<
  826. {
  827. houseId: string
  828. },
  829. BaseResponse
  830. >('/acontractInfo/returnRent', {
  831. params: {
  832. houseId: propertyInfo.value.id,
  833. },
  834. })
  835. if (res.code !== 200) {
  836. ElMessage.error(res.msg)
  837. return
  838. }
  839. ElMessage.success(res.msg)
  840. showTerminateDialog.value = false
  841. await getData()
  842. } catch (error) {
  843. console.error(error)
  844. ElMessage.error('退租失败,请重试')
  845. } finally {
  846. terminating.value = false
  847. }
  848. }
  849. const getData = async () => {
  850. try {
  851. loading.value = true
  852. const res = await clientGet<
  853. {
  854. houseId: string
  855. },
  856. HouseInfoResponse
  857. >('/asimplifiedHouseInfo/getHouseDetailInfo', {
  858. params: {
  859. houseId: route.query.houseId as string,
  860. },
  861. })
  862. if (res.code !== 200) {
  863. ElMessage.error(res.msg)
  864. return
  865. }
  866. const data = res.data
  867. console.log('接口返回数据:', data)
  868. propertyInfo.value = {
  869. id: route.query.houseId as string,
  870. buildingNumber: data.houseName || '',
  871. priceRange: data.rentRange ? `${data.rentRange}` : '',
  872. address: data.address || '',
  873. area: data.houseInfo?.area || '',
  874. status: data.status || '未租',
  875. floor: data.houseInfo?.floor || '',
  876. facilities: data.houseInfo?.introduce || '',
  877. assetType: data.assetType || '',
  878. }
  879. if (data.houseInfo) {
  880. basicInfo.value = {
  881. buildYear: '',
  882. orientation: '',
  883. houseType: data.houseInfo.houseType || '',
  884. }
  885. }
  886. if (data.deviceInfo && Array.isArray(data.deviceInfo)) {
  887. assets.value = data.deviceInfo.map((device) => ({
  888. deviceName: device.deviceName,
  889. deviceType: device.deviceType,
  890. deviceBrand: device.deviceBrand,
  891. deviceNumber: device.deviceNumber,
  892. devicePrice: device.devicePrice,
  893. }))
  894. } else {
  895. assets.value = []
  896. }
  897. if (data.status === '已租' && data.tenantInfo) {
  898. tenantInfo.value = {
  899. tenantName: data.tenantInfo.tenantName,
  900. tenantNumber: data.tenantInfo.tenantNumber,
  901. tenantIdCard: data.tenantInfo.tenantIdCard,
  902. tenantInDate: data.tenantInfo.tenantInDate,
  903. tenantTime: data.tenantInfo.tenantTime,
  904. tenantRent: data.tenantInfo.tenantRent,
  905. }
  906. } else {
  907. tenantInfo.value = null
  908. }
  909. if (data.status === '已租' && data.contractInfo) {
  910. contractInfo.value = {
  911. contractNumber: data.contractInfo.contractNumber,
  912. contractDate: data.contractInfo.contractDate,
  913. contractTime: data.contractInfo.contractTime,
  914. contractExpirationDate: data.contractInfo.contractExpirationDate,
  915. contractDeposit: data.contractInfo.contractDeposit,
  916. contractStatus: data.contractInfo.contractStatus,
  917. contractOriginalUrl: data.contractInfo.contractOriginalUrl,
  918. }
  919. } else {
  920. contractInfo.value = null
  921. }
  922. } catch (error) {
  923. console.error('获取房屋详情失败:', error)
  924. ElMessage.error('获取房屋详情失败')
  925. } finally {
  926. loading.value = false
  927. }
  928. }
  929. onMounted(() => {
  930. getData()
  931. })
  932. </script>
  933. <template>
  934. <el-container class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
  935. <el-main class="h-full" v-loading="loading">
  936. <!-- <div class="py-0 has-footer">-->
  937. <el-button
  938. type="primary"
  939. @click="router.back"
  940. >
  941. <el-icon class="mr-1"><ArrowLeft /></el-icon>
  942. 返回
  943. </el-button>
  944. <!-- </div>-->
  945. <div class="bg-gradient-to-r from-blue-600 to-purple-700 text-white py-4 px-8 my-4 rounded-2xl">
  946. <div class="w-full">
  947. <div class="flex items-center justify-between">
  948. <div>
  949. <h1 class="text-3xl font-extrabold mb-2 my-0">{{ propertyInfo.buildingNumber }}</h1>
  950. <p class="text-base opacity-90">
  951. <el-icon class="mr-2 text-blue-500"><MapPin /></el-icon>
  952. <span>{{ propertyInfo.address }}</span>
  953. </p>
  954. </div>
  955. <div class="text-right flex items-center gap-6">
  956. <div class="relative">
  957. <!-- <button-->
  958. <!-- @click="openARViewing"-->
  959. <!-- class="ar-viewing-btn group relative overflow-hidden bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 text-white px-6 py-3 rounded-2xl font-semibold text-sm shadow-2xl transform transition-all duration-300 hover:scale-105 hover:shadow-pink-500/25 active:scale-95"-->
  960. <!-- >-->
  961. <!-- <div-->
  962. <!-- class="absolute inset-0 bg-gradient-to-r from-pink-600 via-purple-600 to-indigo-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"-->
  963. <!-- ></div>-->
  964. <!-- <div class="relative flex items-center gap-2">-->
  965. <!-- <div class="w-5 h-5 relative">-->
  966. <!-- <div-->
  967. <!-- class="absolute inset-0 bg-white rounded-full opacity-20 animate-ping"-->
  968. <!-- ></div>-->
  969. <!-- <el-icon class="relative z-10"><Camera /></el-icon>-->
  970. <!-- </div>-->
  971. <!-- <span class="relative z-10">AR看房</span>-->
  972. <!-- </div>-->
  973. <!-- <div-->
  974. <!-- class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -skew-x-12 -translate-x-full group-hover:translate-x-full transition-transform duration-1000"-->
  975. <!-- ></div>-->
  976. <!-- </button>-->
  977. <div
  978. class="absolute -inset-1 bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 rounded-2xl blur opacity-30 group-hover:opacity-50 transition-opacity duration-300 -z-10"
  979. ></div>
  980. </div>
  981. <div>
  982. <el-tag
  983. :type="propertyInfo.status === '未租' ? 'success' : 'warning'"
  984. size="large"
  985. class="mb-2"
  986. >
  987. {{ propertyInfo.status }}
  988. </el-tag>
  989. <div class="text-xl font-bold">{{ propertyInfo.priceRange }} 元/月</div>
  990. </div>
  991. </div>
  992. </div>
  993. </div>
  994. </div>
  995. <div class="py-0">
  996. <div class="w-full">
  997. <el-card
  998. v-if="propertyInfo.status === '已租' && tenantInfo"
  999. class="mb-6 shadow-lg rounded-2xl border-0"
  1000. >
  1001. <template #header>
  1002. <div class="flex items-center">
  1003. <el-icon class="mr-2 text-blue-500"><User /></el-icon>
  1004. <span class="text-lg font-semibold">租户信息</span>
  1005. </div>
  1006. </template>
  1007. <el-row :gutter="24">
  1008. <el-col :xs="24" :sm="12" :md="8">
  1009. <div class="mb-4">
  1010. <label class="block text-sm font-medium text-gray-700 mb-1">租户姓名</label>
  1011. <div class="text-lg font-semibold text-gray-800">{{ tenantInfo.tenantName }}</div>
  1012. </div>
  1013. </el-col>
  1014. <el-col :xs="24" :sm="12" :md="8">
  1015. <div class="mb-4">
  1016. <label class="block text-sm font-medium text-gray-700 mb-1">联系电话</label>
  1017. <div class="text-lg font-semibold text-gray-800">{{ tenantInfo.tenantNumber }}</div>
  1018. </div>
  1019. </el-col>
  1020. <el-col :xs="24" :sm="12" :md="8">
  1021. <div class="mb-4">
  1022. <label class="block text-sm font-medium text-gray-700 mb-1">身份证号</label>
  1023. <div class="text-lg font-semibold text-gray-800">{{ tenantInfo.tenantIdCard }}</div>
  1024. </div>
  1025. </el-col>
  1026. <el-col :xs="24" :sm="12" :md="8">
  1027. <div class="mb-4">
  1028. <label class="block text-sm font-medium text-gray-700 mb-1">入住时间</label>
  1029. <div class="text-lg font-semibold text-gray-800">{{ tenantInfo.tenantInDate }}</div>
  1030. </div>
  1031. </el-col>
  1032. <el-col :xs="24" :sm="12" :md="8">
  1033. <div class="mb-4">
  1034. <label class="block text-sm font-medium text-gray-700 mb-1">租期</label>
  1035. <div class="text-lg font-semibold text-gray-800">{{ tenantInfo.tenantTime }}</div>
  1036. </div>
  1037. </el-col>
  1038. <el-col :xs="24" :sm="12" :md="8">
  1039. <div class="mb-4">
  1040. <label class="block text-sm font-medium text-gray-700 mb-1">月租金</label>
  1041. <div class="text-lg font-semibold text-blue-600">
  1042. {{ tenantInfo.tenantRent }} 元
  1043. </div>
  1044. </div>
  1045. </el-col>
  1046. </el-row>
  1047. </el-card>
  1048. <el-card class="mb-6 shadow-lg rounded-2xl border-0">
  1049. <template #header>
  1050. <div class="flex items-center">
  1051. <el-icon class="mr-2 text-green-500"><Home /></el-icon>
  1052. <span class="text-lg font-semibold">房屋基本信息</span>
  1053. </div>
  1054. </template>
  1055. <el-row :gutter="24">
  1056. <el-col :xs="24" :sm="12" :md="6">
  1057. <div class="mb-4">
  1058. <label class="block text-sm font-medium text-gray-700 mb-1">建筑面积</label>
  1059. <div class="text-lg font-semibold text-gray-800">{{ propertyInfo.area }} ㎡</div>
  1060. </div>
  1061. </el-col>
  1062. <el-col :xs="24" :sm="12" :md="6" v-if="propertyInfo.floor">
  1063. <div class="mb-4">
  1064. <label class="block text-sm font-medium text-gray-700 mb-1">所在楼层</label>
  1065. <div class="text-lg font-semibold text-gray-800">{{ propertyInfo.floor }}</div>
  1066. </div>
  1067. </el-col>
  1068. <el-col :xs="24" :sm="12" :md="6">
  1069. <div class="mb-4">
  1070. <label class="block text-sm font-medium text-gray-700 mb-1">介绍</label>
  1071. <div class="text-lg font-semibold text-gray-800">{{ propertyInfo.facilities }}</div>
  1072. </div>
  1073. </el-col>
  1074. <el-col :xs="24" :sm="12" :md="6" v-if="basicInfo.houseType">
  1075. <div class="mb-4">
  1076. <label class="block text-sm font-medium text-gray-700 mb-1">房屋类型</label>
  1077. <div class="text-lg font-semibold text-gray-800">{{ basicInfo.houseType }}</div>
  1078. </div>
  1079. </el-col>
  1080. </el-row>
  1081. </el-card>
  1082. <el-card class="mb-6 shadow-lg rounded-2xl border-0">
  1083. <template #header>
  1084. <div class="flex items-center justify-between">
  1085. <div class="flex items-center">
  1086. <el-icon class="mr-2 text-purple-500"><Cpu /></el-icon>
  1087. <span class="text-lg font-semibold">房屋设备资产</span>
  1088. </div>
  1089. <div class="flex items-center gap-4">
  1090. <el-select
  1091. v-model="assetFilter.category"
  1092. placeholder="设备类型"
  1093. size="small"
  1094. style="width: 120px"
  1095. @change="filterAssets"
  1096. >
  1097. <el-option label="全部" value=""></el-option>
  1098. <el-option
  1099. v-for="type in deviceTypes"
  1100. :key="type"
  1101. :label="type"
  1102. :value="type"
  1103. ></el-option>
  1104. </el-select>
  1105. </div>
  1106. </div>
  1107. </template>
  1108. <el-row :gutter="16">
  1109. <el-col
  1110. :xs="24"
  1111. :sm="12"
  1112. :md="8"
  1113. :lg="6"
  1114. v-for="(asset, index) in filteredAssets"
  1115. :key="index"
  1116. class="mb-4"
  1117. >
  1118. <div
  1119. class="border border-gray-200 rounded-lg px-4 py-2 hover:shadow-md transition-shadow bg-white"
  1120. >
  1121. <div class="mb-3">
  1122. <span class="font-semibold text-gray-800 text-lg">{{ asset.deviceName }}</span>
  1123. <p class="text-sm text-gray-500">{{ asset.deviceType }}</p>
  1124. </div>
  1125. <div class="text-sm text-gray-600 space-y-1">
  1126. <p><strong>品牌:</strong>{{ asset.deviceBrand }}</p>
  1127. <p><strong>型号:</strong>{{ asset.deviceNumber }}</p>
  1128. <p>
  1129. <strong>价值:</strong>
  1130. <span class="text-green-600 font-semibold">{{ asset.devicePrice }} 元</span>
  1131. </p>
  1132. </div>
  1133. </div>
  1134. </el-col>
  1135. </el-row>
  1136. <div v-if="filteredAssets.length === 0" class="text-center py-8 text-gray-500">
  1137. 暂无符合条件的设备资产
  1138. </div>
  1139. </el-card>
  1140. <el-card
  1141. v-if="propertyInfo.status === '已租' && contractInfo"
  1142. class="mb-6 shadow-lg rounded-2xl border-0"
  1143. >
  1144. <template #header>
  1145. <div class="flex items-center justify-between">
  1146. <div class="flex items-center">
  1147. <el-icon class="mr-2 text-purple-500"><FileText /></el-icon>
  1148. <span class="text-lg font-semibold">合同信息</span>
  1149. </div>
  1150. <!-- 新增:预览合同按钮 -->
  1151. <el-button
  1152. v-if="contractInfo.contractOriginalUrl"
  1153. type="primary"
  1154. size="small"
  1155. @click="previewDocx(contractInfo.contractOriginalUrl)"
  1156. class="flex items-center gap-1"
  1157. >
  1158. <el-icon><Eye /></el-icon>
  1159. 预览合同
  1160. </el-button>
  1161. </div>
  1162. </template>
  1163. <el-row :gutter="24">
  1164. <el-col :xs="24" :sm="12" :md="8">
  1165. <div class="mb-4">
  1166. <label class="block text-sm font-medium text-gray-700 mb-1">合同编号</label>
  1167. <div class="text-lg font-semibold text-gray-800">{{ contractInfo.contractNumber }}</div>
  1168. </div>
  1169. </el-col>
  1170. <el-col :xs="24" :sm="12" :md="8">
  1171. <div class="mb-4">
  1172. <label class="block text-sm font-medium text-gray-700 mb-1">签约日期</label>
  1173. <div class="text-lg font-semibold text-gray-800">{{ contractInfo.contractDate }}</div>
  1174. </div>
  1175. </el-col>
  1176. <el-col :xs="24" :sm="12" :md="8">
  1177. <div class="mb-4">
  1178. <label class="block text-sm font-medium text-gray-700 mb-1">合同期限</label>
  1179. <div class="text-lg font-semibold text-gray-800">{{ contractInfo.contractTime }}</div>
  1180. </div>
  1181. </el-col>
  1182. <el-col :xs="24" :sm="12" :md="8">
  1183. <div class="mb-4">
  1184. <label class="block text-sm font-medium text-gray-700 mb-1">到期日期</label>
  1185. <div class="text-lg font-semibold text-gray-800">{{ contractInfo.contractExpirationDate }}</div>
  1186. </div>
  1187. </el-col>
  1188. <el-col :xs="24" :sm="12" :md="8">
  1189. <div class="mb-4">
  1190. <label class="block text-sm font-medium text-gray-700 mb-1">押金</label>
  1191. <div class="text-lg font-semibold text-gray-800">{{ contractInfo.contractDeposit }} 元</div>
  1192. </div>
  1193. </el-col>
  1194. <el-col :xs="24" :sm="12" :md="8">
  1195. <div class="mb-4">
  1196. <label class="block text-sm font-medium text-gray-700 mb-1">合同状态</label>
  1197. <div class="text-lg font-semibold text-gray-800">{{ contractInfo.contractStatus }}</div>
  1198. </div>
  1199. </el-col>
  1200. </el-row>
  1201. </el-card>
  1202. <div class="flex justify-center gap-4">
  1203. <el-button
  1204. v-if="propertyInfo.status === '空闲'"
  1205. type="primary"
  1206. size="large"
  1207. @click="showRentalDialog = true"
  1208. class="px-8 py-3"
  1209. >
  1210. <el-icon class="mr-2"><Plus /></el-icon>
  1211. 出租房屋
  1212. </el-button>
  1213. <el-button
  1214. v-if="propertyInfo.status === '已租'"
  1215. type="danger"
  1216. size="large"
  1217. @click="showTerminateDialog = true"
  1218. class="px-8 py-3"
  1219. >
  1220. <el-icon class="mr-2"><X /></el-icon>
  1221. 退租房屋
  1222. </el-button>
  1223. </div>
  1224. </div>
  1225. </div>
  1226. </el-main>
  1227. <!-- 新增:DOCX预览对话框 -->
  1228. <el-dialog
  1229. v-model="showDocxPreviewDialog"
  1230. title="合同预览"
  1231. width="70%"
  1232. :before-close="closeDocxPreview"
  1233. class="docx-preview-dialog"
  1234. >
  1235. <div
  1236. v-loading="docxPreviewLoading"
  1237. element-loading-text="正在加载合同文件..."
  1238. class="docx-preview-wrapper"
  1239. >
  1240. <div ref="docxPreviewContainer" class="docx-container" v-show="!docxPreviewLoading"></div>
  1241. </div>
  1242. <template #footer>
  1243. <div class="flex justify-end">
  1244. <el-button @click="closeDocxPreview">关闭</el-button>
  1245. </div>
  1246. </template>
  1247. </el-dialog>
  1248. <el-dialog
  1249. v-model="showRentalDialog"
  1250. title="房屋出租"
  1251. width="800px"
  1252. :before-close="handleCloseRentalDialog"
  1253. >
  1254. <el-form :model="rentalForm" :rules="rentalRules" ref="rentalFormRef" label-width="120px">
  1255. <el-steps :active="currentStep" finish-status="success" class="mb-8">
  1256. <el-step title="租户信息" />
  1257. <el-step title="合同条款" />
  1258. <el-step title="确认提交" />
  1259. </el-steps>
  1260. <div v-show="currentStep === 0">
  1261. <el-form-item label="租户姓名" prop="rentalInfo.rentalName">
  1262. <el-input v-model="rentalForm.rentalInfo.rentalName" placeholder="请输入租户姓名" />
  1263. </el-form-item>
  1264. <el-form-item label="联系电话" prop="rentalInfo.rentalPhone">
  1265. <el-input v-model="rentalForm.rentalInfo.rentalPhone" placeholder="请输入联系电话" />
  1266. </el-form-item>
  1267. <el-form-item label="身份证号" prop="rentalInfo.rentalIdCard">
  1268. <el-input v-model="rentalForm.rentalInfo.rentalIdCard" placeholder="请输入身份证号" />
  1269. </el-form-item>
  1270. <el-form-item label="租期开始时间" prop="rentalInfo.rentalTimeStart">
  1271. <el-date-picker
  1272. type="date"
  1273. format="YYYY-MM-DD"
  1274. value-format="YYYY-MM-DD"
  1275. clearable
  1276. v-model="rentalForm.rentalInfo.rentalTimeStart"
  1277. placeholder="请输入租期开始时间"
  1278. />
  1279. </el-form-item>
  1280. <el-form-item label="租期" prop="rentalInfo.rentalTime">
  1281. <el-input v-model="rentalForm.rentalInfo.rentalTime" placeholder="请输入租期" />
  1282. </el-form-item>
  1283. <el-form-item label="租金" prop="rentalInfo.rentalRect">
  1284. <el-input v-model="rentalForm.rentalInfo.rentalRect" placeholder="请输入租金" />
  1285. </el-form-item>
  1286. </div>
  1287. <div v-show="currentStep === 1">
  1288. <div v-if="propertyInfo.assetType === '公租房'">
  1289. <el-form-item label="租方" prop="contractDataA.tenantName">
  1290. <el-input v-model="rentalForm.contractDataA.tenantName" placeholder="请输入租方" />
  1291. </el-form-item>
  1292. <el-form-item label="房东" prop="contractDataA.landlordName">
  1293. <el-input v-model="rentalForm.contractDataA.landlordName" placeholder="请输入房东" />
  1294. </el-form-item>
  1295. <el-form-item label="租期开始时间" prop="contractDataA.tenantTimeStart">
  1296. <el-date-picker
  1297. type="date"
  1298. format="YYYY-MM-DD"
  1299. value-format="YYYY-MM-DD"
  1300. clearable
  1301. v-model="rentalForm.contractDataA.tenantTimeStart"
  1302. placeholder="请输入租期开始时间"
  1303. />
  1304. </el-form-item>
  1305. <el-form-item label="租期" prop="contractDataA.tenantTime">
  1306. <el-input v-model="rentalForm.contractDataA.tenantTime" placeholder="请输入租期" />
  1307. </el-form-item>
  1308. <el-form-item label="租期结束时间" prop="contractDataA.tenantTimeEnd">
  1309. <el-date-picker
  1310. type="date"
  1311. format="YYYY-MM-DD"
  1312. value-format="YYYY-MM-DD"
  1313. clearable
  1314. v-model="rentalForm.contractDataA.tenantTimeEnd"
  1315. placeholder="请输入租期结束时间"
  1316. />
  1317. </el-form-item>
  1318. <el-form-item label="每套租金" prop="contractDataA.tenantRent">
  1319. <el-input
  1320. v-model="rentalForm.contractDataA.tenantRent"
  1321. placeholder="请输入每套租金"
  1322. />
  1323. </el-form-item>
  1324. <el-form-item label="每套物业费" prop="contractDataA.tenantDeposit">
  1325. <el-input
  1326. v-model="rentalForm.contractDataA.tenantDeposit"
  1327. placeholder="请输入每套物业费"
  1328. />
  1329. </el-form-item>
  1330. <el-form-item label="押金" prop="contractDataA.tenantDepositAmount">
  1331. <el-input-number
  1332. v-model="rentalForm.contractDataA.tenantDepositAmount"
  1333. placeholder="请输入押金"
  1334. />
  1335. </el-form-item>
  1336. </div>
  1337. <div v-if="propertyInfo.assetType === '厂房'">
  1338. <el-form-item label="用途" prop="contractDataB.purpose">
  1339. <el-input v-model="rentalForm.contractDataB.purpose" placeholder="请输入用途" />
  1340. </el-form-item>
  1341. <el-form-item label="房东" prop="contractDataB.landlordName">
  1342. <el-input v-model="rentalForm.contractDataB.landlordName" placeholder="请输入房东" />
  1343. </el-form-item>
  1344. <el-form-item label="法人统一社会信用代码" prop="contractDataB.landlordUniCode">
  1345. <el-input
  1346. v-model="rentalForm.contractDataB.landlordUniCode"
  1347. placeholder="请输入法人统一社会信用代码"
  1348. />
  1349. </el-form-item>
  1350. <el-form-item label="法定代表人" prop="contractDataB.landlordLegalRepresentative">
  1351. <el-input
  1352. v-model="rentalForm.contractDataB.landlordLegalRepresentative"
  1353. placeholder="请输入法定代表人"
  1354. />
  1355. </el-form-item>
  1356. <el-form-item label="房东职务" prop="contractDataB.landlordDuty">
  1357. <el-input
  1358. v-model="rentalForm.contractDataB.landlordDuty"
  1359. placeholder="请输入房东职务"
  1360. />
  1361. </el-form-item>
  1362. <el-form-item label="租方" prop="contractDataB.tenantName">
  1363. <el-input v-model="rentalForm.contractDataB.tenantName" placeholder="请输入租方" />
  1364. </el-form-item>
  1365. <el-form-item label="租方统一社会信用代码" prop="contractDataB.tenantUniCode">
  1366. <el-input
  1367. v-model="rentalForm.contractDataB.tenantUniCode"
  1368. placeholder="请输入租方统一社会信用代码"
  1369. />
  1370. </el-form-item>
  1371. <el-form-item label="法定代表人" prop="contractDataB.tenantLegalRepresentative">
  1372. <el-input
  1373. v-model="rentalForm.contractDataB.tenantLegalRepresentative"
  1374. placeholder="请输入法定代表人"
  1375. />
  1376. </el-form-item>
  1377. <el-form-item label="租方职务" prop="contractDataB.tenantDuty">
  1378. <el-input
  1379. v-model="rentalForm.contractDataB.tenantDuty"
  1380. placeholder="请输入租方职务"
  1381. />
  1382. </el-form-item>
  1383. <el-form-item label="谁" prop="contractDataB.who">
  1384. <el-input v-model="rentalForm.contractDataB.who" placeholder="请输入谁" />
  1385. </el-form-item>
  1386. <el-form-item label="何时" prop="contractDataB.whatTime">
  1387. <el-input v-model="rentalForm.contractDataB.whatTime" placeholder="请输入何时" />
  1388. </el-form-item>
  1389. <el-form-item label="标题" prop="contractDataB.title">
  1390. <el-input v-model="rentalForm.contractDataB.title" placeholder="请输入标题" />
  1391. </el-form-item>
  1392. <el-form-item label="配套用房面积 (㎡) " prop="contractDataB.supportingRoomArea">
  1393. <el-input
  1394. v-model="rentalForm.contractDataB.supportingRoomArea"
  1395. placeholder="请输入配套用房面积(㎡)"
  1396. />
  1397. </el-form-item>
  1398. <el-form-item label="办公室面积(㎡)" prop="contractDataB.officeArea">
  1399. <el-input
  1400. v-model="rentalForm.contractDataB.officeArea"
  1401. placeholder="请输入办公室面积(㎡)"
  1402. />
  1403. </el-form-item>
  1404. <el-form-item label="食堂面积(㎡)" prop="contractDataB.canteenArea">
  1405. <el-input
  1406. v-model="rentalForm.contractDataB.canteenArea"
  1407. placeholder="请输入食堂面积(㎡)"
  1408. />
  1409. </el-form-item>
  1410. <el-form-item label="总面积(㎡)" prop="contractDataB.totalLeasedArea">
  1411. <el-input
  1412. v-model="rentalForm.contractDataB.totalLeasedArea"
  1413. placeholder="请输入总面积(㎡)"
  1414. />
  1415. </el-form-item>
  1416. <el-form-item label="宿舍楼总间数" prop="contractDataB.dormTotalRooms">
  1417. <el-input
  1418. v-model="rentalForm.contractDataB.dormTotalRooms"
  1419. placeholder="请输入宿舍楼总间数"
  1420. />
  1421. </el-form-item>
  1422. <el-form-item label="宿舍小间数" prop="contractDataB.dormSmallRooms">
  1423. <el-input
  1424. v-model="rentalForm.contractDataB.dormSmallRooms"
  1425. placeholder="请输入宿舍小间数"
  1426. />
  1427. </el-form-item>
  1428. <el-form-item label="宿舍大间数" prop="contractDataB.dormLargeRooms">
  1429. <el-input
  1430. v-model="rentalForm.contractDataB.dormLargeRooms"
  1431. placeholder="请输入宿舍大间数"
  1432. />
  1433. </el-form-item>
  1434. <el-form-item label="标的交付日期" prop="contractDataB.deliveryDate">
  1435. <el-input
  1436. v-model="rentalForm.contractDataB.deliveryDate"
  1437. placeholder="请输入标的交付日期"
  1438. />
  1439. </el-form-item>
  1440. <el-form-item label="生产项目名称" prop="contractDataB.productionProject">
  1441. <el-input
  1442. v-model="rentalForm.contractDataB.productionProject"
  1443. placeholder="请输入生产项目名称"
  1444. />
  1445. </el-form-item>
  1446. <el-form-item label="租赁期限(年)" prop="contractDataB.leaseTermYears">
  1447. <el-input
  1448. v-model="rentalForm.contractDataB.leaseTermYears"
  1449. placeholder="请输入租赁期限(年)"
  1450. />
  1451. </el-form-item>
  1452. <el-form-item label="租赁起始日" prop="contractDataB.leaseStartDate">
  1453. <el-date-picker
  1454. type="date"
  1455. format="YYYY-MM-DD"
  1456. value-format="YYYY-MM-DD"
  1457. clearable
  1458. v-model="rentalForm.contractDataB.leaseStartDate"
  1459. placeholder="请输入租赁起始日"
  1460. />
  1461. </el-form-item>
  1462. <el-form-item label="租赁终止日" prop="contractDataB.leaseEndDate">
  1463. <el-date-picker
  1464. type="date"
  1465. format="YYYY-MM-DD"
  1466. value-format="YYYY-MM-DD"
  1467. clearable
  1468. v-model="rentalForm.contractDataB.leaseEndDate"
  1469. placeholder="请输入租赁终止日"
  1470. />
  1471. </el-form-item>
  1472. <el-form-item label="续租提前通知月数" prop="contractDataB.renewNoticeMonths">
  1473. <el-input
  1474. v-model="rentalForm.contractDataB.renewNoticeMonths"
  1475. placeholder="请输入续租提前通知月数"
  1476. />
  1477. </el-form-item>
  1478. <el-form-item label="标准化厂房某楼层" prop="contractDataB.factorySpecificFloor1">
  1479. <el-input
  1480. v-model="rentalForm.contractDataB.factorySpecificFloor1"
  1481. placeholder="请输入标准化厂房某楼层"
  1482. />
  1483. </el-form-item>
  1484. <el-form-item label="该楼层租金(元/㎡/月)" prop="contractDataB.rentPerSqmFloor1">
  1485. <el-input
  1486. v-model="rentalForm.contractDataB.rentPerSqmFloor1"
  1487. placeholder="请输入该楼层租金(元/㎡/月)"
  1488. />
  1489. </el-form-item>
  1490. <el-form-item label="标准化厂房另一楼层" prop="contractDataB.factorySpecificFloor2">
  1491. <el-input
  1492. v-model="rentalForm.contractDataB.factorySpecificFloor2"
  1493. placeholder="请输入标准化厂房另一楼层"
  1494. />
  1495. </el-form-item>
  1496. <el-form-item label="该楼层租金(元/㎡/月)" prop="contractDataB.rentPerSqmFloor2">
  1497. <el-input
  1498. v-model="rentalForm.contractDataB.rentPerSqmFloor2"
  1499. placeholder="请输入该楼层租金(元/㎡/月)"
  1500. />
  1501. </el-form-item>
  1502. <el-form-item label="厂房年租金(元)" prop="contractDataB.factoryAnnualRent">
  1503. <el-input
  1504. v-model="rentalForm.contractDataB.factoryAnnualRent"
  1505. placeholder="请输入厂房年租金(元)"
  1506. />
  1507. </el-form-item>
  1508. <el-form-item
  1509. label="配套用房租金(元/㎡/月)"
  1510. prop="contractDataB.supportingRoomRentPerSqm"
  1511. >
  1512. <el-input
  1513. v-model="rentalForm.contractDataB.supportingRoomRentPerSqm"
  1514. placeholder="请输入配套用房租金(元/㎡/月)"
  1515. />
  1516. </el-form-item>
  1517. <el-form-item
  1518. label="配套用房年租金(元)"
  1519. prop="contractDataB.supportingRoomAnnualRent"
  1520. >
  1521. <el-input
  1522. v-model="rentalForm.contractDataB.supportingRoomAnnualRent"
  1523. placeholder="请输入配套用房年租金(元)"
  1524. />
  1525. </el-form-item>
  1526. <el-form-item label="宿舍楼普通单间月租金(元)" prop="contractDataB.dormSmallRoomRent">
  1527. <el-input
  1528. v-model="rentalForm.contractDataB.dormSmallRoomRent"
  1529. placeholder="请输入宿舍楼普通单间月租金(元)"
  1530. />
  1531. </el-form-item>
  1532. <el-form-item label="宿舍楼大单间月租金(元)" prop="contractDataB.dormLargeRoomRent">
  1533. <el-input
  1534. v-model="rentalForm.contractDataB.dormLargeRoomRent"
  1535. placeholder="请输入宿舍楼大单间月租金(元)"
  1536. />
  1537. </el-form-item>
  1538. <el-form-item label="宿舍楼年租金(元)" prop="contractDataB.dormAnnualRent">
  1539. <el-input
  1540. v-model="rentalForm.contractDataB.dormAnnualRent"
  1541. placeholder="请输入宿舍楼年租金(元)"
  1542. />
  1543. </el-form-item>
  1544. <el-form-item label="总租金(元)" prop="contractDataB.totalAnnualRent">
  1545. <el-input
  1546. v-model="rentalForm.contractDataB.totalAnnualRent"
  1547. placeholder="请输入总租金(元)"
  1548. />
  1549. </el-form-item>
  1550. <el-form-item
  1551. label="标准化厂房和配套用房物业费(元/㎡/月)"
  1552. prop="contractDataB.propertyFeePerSqm"
  1553. >
  1554. <el-input
  1555. v-model="rentalForm.contractDataB.propertyFeePerSqm"
  1556. placeholder="请输入标准化厂房和配套用房物业费(元/㎡/月)"
  1557. />
  1558. </el-form-item>
  1559. <el-form-item
  1560. label="标准化厂房和配套用房年物业费(元)"
  1561. prop="contractDataB.factorySupportingPropertyFee"
  1562. >
  1563. <el-input
  1564. v-model="rentalForm.contractDataB.factorySupportingPropertyFee"
  1565. placeholder="请输入标准化厂房和配套用房年物业费(元)"
  1566. />
  1567. </el-form-item>
  1568. <el-form-item
  1569. label="宿舍楼普通单间月物业费(元)"
  1570. prop="contractDataB.dormSmallRoomPropertyFee"
  1571. >
  1572. <el-input
  1573. v-model="rentalForm.contractDataB.dormSmallRoomPropertyFee"
  1574. placeholder="请输入宿舍楼普通单间月物业费(元)"
  1575. />
  1576. </el-form-item>
  1577. <el-form-item
  1578. label="宿舍楼大单间月物业费(元)"
  1579. prop="contractDataB.dormLargeRoomPropertyFee"
  1580. >
  1581. <el-input
  1582. v-model="rentalForm.contractDataB.dormLargeRoomPropertyFee"
  1583. placeholder="请输入宿舍楼大单间月物业费(元)"
  1584. />
  1585. </el-form-item>
  1586. <el-form-item label="宿舍楼年物业费(元)" prop="contractDataB.dormAnnualPropertyFee">
  1587. <el-input
  1588. v-model="rentalForm.contractDataB.dormAnnualPropertyFee"
  1589. placeholder="请输入宿舍楼年物业费(元)"
  1590. />
  1591. </el-form-item>
  1592. <el-form-item label="年物业管理费(元)" prop="contractDataB.totalAnnualPropertyFee">
  1593. <el-input
  1594. v-model="rentalForm.contractDataB.totalAnnualPropertyFee"
  1595. placeholder="请输入年物业管理费(元)"
  1596. />
  1597. </el-form-item>
  1598. <el-form-item
  1599. label="年租金和物业费用总计(元)"
  1600. prop="contractDataB.annualTotalRentAndPropertyFee"
  1601. >
  1602. <el-input
  1603. v-model="rentalForm.contractDataB.annualTotalRentAndPropertyFee"
  1604. placeholder="请输入年租金和物业费用总计(元)"
  1605. />
  1606. </el-form-item>
  1607. <el-form-item label="租赁押金金额(元)" prop="contractDataB.depositAmount">
  1608. <el-input-number
  1609. v-model="rentalForm.contractDataB.depositAmount"
  1610. placeholder="请输入租赁押金金额(元)"
  1611. />
  1612. </el-form-item>
  1613. </div>
  1614. <div v-if="propertyInfo.assetType === '创新创业基地'">
  1615. <el-form-item label="出租方(甲方)名称" prop="contractDataC.lessorName">
  1616. <el-input
  1617. v-model="rentalForm.contractDataC.lessorName"
  1618. placeholder="请输入出租方(甲方)名称"
  1619. />
  1620. </el-form-item>
  1621. <el-form-item label="出租方法定代表人" prop="contractDataC.lessorLegalRep">
  1622. <el-input
  1623. v-model="rentalForm.contractDataC.lessorLegalRep"
  1624. placeholder="请输入出租方法定代表人"
  1625. />
  1626. </el-form-item>
  1627. <el-form-item label="出租方统一社会信用代码" prop="contractDataC.lessorUniCode">
  1628. <el-input
  1629. v-model="rentalForm.contractDataC.lessorUniCode"
  1630. placeholder="请输入出租方统一社会信用代码"
  1631. />
  1632. </el-form-item>
  1633. <el-form-item label="承租方(乙方)名称" prop="contractDataC.lesseeName">
  1634. <el-input
  1635. v-model="rentalForm.contractDataC.lesseeName"
  1636. placeholder="请输入承租方(乙方)名称"
  1637. />
  1638. </el-form-item>
  1639. <el-form-item label="承租方法定代表人" prop="contractDataC.lesseeLegalRep">
  1640. <el-input
  1641. v-model="rentalForm.contractDataC.lesseeLegalRep"
  1642. placeholder="请输入承租方法定代表人"
  1643. />
  1644. </el-form-item>
  1645. <el-form-item label="承租方统一社会信用代码/身份证号" prop="contractDataC.lesseeIdCode">
  1646. <el-input
  1647. v-model="rentalForm.contractDataC.lesseeIdCode"
  1648. placeholder="请输入承租方统一社会信用代码/身份证号"
  1649. />
  1650. </el-form-item>
  1651. <el-form-item label="标的物坐落" prop="contractDataC.propertyLocation">
  1652. <el-input
  1653. v-model="rentalForm.contractDataC.propertyLocation"
  1654. placeholder="请输入标的物坐落"
  1655. />
  1656. </el-form-item>
  1657. <el-form-item label="计租面积(平方米)" prop="contractDataC.rentableArea">
  1658. <el-input
  1659. v-model="rentalForm.contractDataC.rentableArea"
  1660. placeholder="请输入计租面积(平方米)"
  1661. />
  1662. </el-form-item>
  1663. <el-form-item label="使用面积(平方米)" prop="contractDataC.usableArea">
  1664. <el-input
  1665. v-model="rentalForm.contractDataC.usableArea"
  1666. placeholder="请输入使用面积(平方米)"
  1667. />
  1668. </el-form-item>
  1669. <el-form-item label="公摊面积(平方米)" prop="contractDataC.sharedArea">
  1670. <el-input
  1671. v-model="rentalForm.contractDataC.sharedArea"
  1672. placeholder="请输入公摊面积(平方米)"
  1673. />
  1674. </el-form-item>
  1675. <el-form-item label="标的物使用性质" prop="contractDataC.propertyUseNature">
  1676. <el-input
  1677. v-model="rentalForm.contractDataC.propertyUseNature"
  1678. placeholder="请输入标的物使用性质"
  1679. />
  1680. </el-form-item>
  1681. <el-form-item label="店铺名称" prop="contractDataC.storeName">
  1682. <el-input v-model="rentalForm.contractDataC.storeName" placeholder="请输入店铺名称" />
  1683. </el-form-item>
  1684. <el-form-item label="商品/服务品牌" prop="contractDataC.brand">
  1685. <el-input
  1686. v-model="rentalForm.contractDataC.brand"
  1687. placeholder="请输入商品/服务品牌"
  1688. />
  1689. </el-form-item>
  1690. <el-form-item label="经营范围" prop="contractDataC.businessScope">
  1691. <el-input
  1692. v-model="rentalForm.contractDataC.businessScope"
  1693. placeholder="请输入经营范围"
  1694. />
  1695. </el-form-item>
  1696. <el-form-item label="租赁期限(个月)" prop="contractDataC.leaseTermMonths">
  1697. <el-input
  1698. v-model="rentalForm.contractDataC.leaseTermMonths"
  1699. placeholder="请输入租赁期限(个月)"
  1700. />
  1701. </el-form-item>
  1702. <el-form-item label="租赁起始日" prop="contractDataC.leaseStartDate">
  1703. <el-date-picker
  1704. type="date"
  1705. format="YYYY-MM-DD"
  1706. value-format="YYYY-MM-DD"
  1707. clearable
  1708. v-model="rentalForm.contractDataC.leaseStartDate"
  1709. placeholder="请输入租赁起始日"
  1710. />
  1711. </el-form-item>
  1712. <el-form-item label="租赁终止日" prop="contractDataC.leaseEndDate">
  1713. <el-date-picker
  1714. type="date"
  1715. format="YYYY-MM-DD"
  1716. value-format="YYYY-MM-DD"
  1717. clearable
  1718. v-model="rentalForm.contractDataC.leaseEndDate"
  1719. placeholder="请输入租赁终止日"
  1720. />
  1721. </el-form-item>
  1722. <el-form-item label="装修期起始日" prop="contractDataC.renovationStartDate">
  1723. <el-input
  1724. v-model="rentalForm.contractDataC.renovationStartDate"
  1725. placeholder="请输入装修期起始日"
  1726. />
  1727. </el-form-item>
  1728. <el-form-item label="装修期终止日" prop="contractDataC.renovationEndDate">
  1729. <el-input
  1730. v-model="rentalForm.contractDataC.renovationEndDate"
  1731. placeholder="请输入装修期终止日"
  1732. />
  1733. </el-form-item>
  1734. <el-form-item label="正式起租时间" prop="contractDataC.officialLeaseStartDate">
  1735. <el-input
  1736. v-model="rentalForm.contractDataC.officialLeaseStartDate"
  1737. placeholder="请输入正式起租时间"
  1738. />
  1739. </el-form-item>
  1740. <el-form-item label="年租金(小写)" prop="contractDataC.annualRentLower">
  1741. <el-input
  1742. v-model="rentalForm.contractDataC.annualRentLower"
  1743. placeholder="请输入年租金(小写)"
  1744. />
  1745. </el-form-item>
  1746. <el-form-item label="年物业服务费(小写)" prop="contractDataC.annualPropertyFeeLower">
  1747. <el-input
  1748. v-model="rentalForm.contractDataC.annualPropertyFeeLower"
  1749. placeholder="请输入年物业服务费(小写)"
  1750. />
  1751. </el-form-item>
  1752. <el-form-item label="租赁保证金金额" prop="contractDataC.depositAmount">
  1753. <el-input-number
  1754. v-model="rentalForm.contractDataC.depositAmount"
  1755. placeholder="请输入租赁保证金金额"
  1756. />
  1757. </el-form-item>
  1758. </div>
  1759. </div>
  1760. <div v-show="currentStep === 2">
  1761. <div class="bg-gray-50 p-6 rounded-lg">
  1762. <h3 class="text-lg font-semibold mb-4">请确认以下信息:</h3>
  1763. <el-row :gutter="24">
  1764. <el-col :span="12">
  1765. <p><strong>租户姓名:</strong>{{ rentalForm.rentalInfo.rentalName }}</p>
  1766. <p><strong>联系电话:</strong>{{ rentalForm.rentalInfo.rentalPhone }}</p>
  1767. <p><strong>身份证号:</strong>{{ rentalForm.rentalInfo.rentalIdCard }}</p>
  1768. </el-col>
  1769. <el-col :span="12">
  1770. <p><strong>租金:</strong>{{ rentalForm.rentalInfo.rentalRect }} 元</p>
  1771. <p><strong>租期:</strong>{{ rentalForm.rentalInfo.rentalTime }}</p>
  1772. <p><strong>开始时间:</strong>{{ rentalForm.rentalInfo.rentalTimeStart }}</p>
  1773. </el-col>
  1774. </el-row>
  1775. </div>
  1776. </div>
  1777. </el-form>
  1778. <template #footer>
  1779. <div class="flex justify-between">
  1780. <el-button v-if="currentStep > 0" @click="currentStep--">上一步</el-button>
  1781. <div class="flex gap-2">
  1782. <el-button @click="handleCloseRentalDialog">取消</el-button>
  1783. <el-button v-if="currentStep < 2" type="primary" @click="nextStep">下一步</el-button>
  1784. <el-button
  1785. v-if="currentStep === 2"
  1786. type="primary"
  1787. @click="submitRental"
  1788. :loading="submitting"
  1789. >确认出租</el-button
  1790. >
  1791. </div>
  1792. </div>
  1793. </template>
  1794. </el-dialog>
  1795. <el-dialog v-model="showTerminateDialog" title="退租确认" width="600px">
  1796. <div class="mb-6" v-if="contractInfo">
  1797. <h3 class="text-lg font-semibold mb-4">合同信息</h3>
  1798. <div class="bg-gray-50 p-4 rounded-lg">
  1799. <p><strong>合同编号:</strong>{{ contractInfo.contractNumber }}</p>
  1800. <p><strong>租户姓名:</strong>{{ tenantInfo?.tenantName }}</p>
  1801. <p><strong>租期:</strong>{{ contractInfo.contractTime }}</p>
  1802. <p><strong>到期日期:</strong>{{ contractInfo.contractExpirationDate }}</p>
  1803. <p><strong>押金:</strong>{{ contractInfo.contractDeposit }} 元</p>
  1804. </div>
  1805. </div>
  1806. <template #footer>
  1807. <div class="flex justify-end gap-2">
  1808. <el-button @click="showTerminateDialog = false">取消</el-button>
  1809. <el-button type="danger" @click="confirmTerminate" :loading="terminating">
  1810. 确认退租
  1811. </el-button>
  1812. </div>
  1813. </template>
  1814. </el-dialog>
  1815. </el-container>
  1816. </template>
  1817. <style scoped>
  1818. .el-card {
  1819. border: none;
  1820. }
  1821. .el-button {
  1822. font-weight: 600;
  1823. }
  1824. .el-steps {
  1825. margin-bottom: 2rem;
  1826. }
  1827. .ar-viewing-btn {
  1828. position: relative;
  1829. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1830. border: none;
  1831. cursor: pointer;
  1832. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  1833. }
  1834. .ar-viewing-btn:hover {
  1835. transform: translateY(-2px) scale(1.05);
  1836. box-shadow: 0 20px 40px rgba(102, 126, 234, 0.4);
  1837. }
  1838. .ar-viewing-btn:active {
  1839. transform: translateY(0) scale(0.98);
  1840. }
  1841. .ar-viewing-btn::before {
  1842. content: '';
  1843. position: absolute;
  1844. top: 0;
  1845. left: 0;
  1846. right: 0;
  1847. bottom: 0;
  1848. background: linear-gradient(135deg, #f093fb 0%, #f5576c 50%, #4facfe 100%);
  1849. border-radius: inherit;
  1850. opacity: 0;
  1851. transition: opacity 0.3s ease;
  1852. z-index: -1;
  1853. }
  1854. .ar-viewing-btn:hover::before {
  1855. opacity: 1;
  1856. }
  1857. @keyframes gradient {
  1858. 0% {
  1859. background-position: 0% 50%;
  1860. }
  1861. 50% {
  1862. background-position: 100% 50%;
  1863. }
  1864. 100% {
  1865. background-position: 0% 50%;
  1866. }
  1867. }
  1868. .bg-gradient-to-r {
  1869. background-size: 200% 200%;
  1870. animation: gradient 15s ease infinite;
  1871. }
  1872. @keyframes ping {
  1873. 75%,
  1874. 100% {
  1875. transform: scale(2);
  1876. opacity: 0;
  1877. }
  1878. }
  1879. .animate-ping {
  1880. animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
  1881. }
  1882. /* 新增:DOCX预览相关样式 */
  1883. .docx-preview-dialog {
  1884. --el-dialog-content-font-size: 14px;
  1885. }
  1886. .docx-preview-wrapper {
  1887. min-height: 400px;
  1888. max-height: 70vh;
  1889. overflow-y: auto;
  1890. border: 1px solid #e4e7ed;
  1891. border-radius: 4px;
  1892. background-color: #fff;
  1893. }
  1894. .docx-container {
  1895. padding: 20px;
  1896. font-family: 'Times New Roman', serif;
  1897. line-height: 1.6;
  1898. color: #333;
  1899. }
  1900. /* DOCX内容样式优化 */
  1901. .docx-container :deep(.docx-wrapper) {
  1902. max-width: 100%;
  1903. margin: 0 auto;
  1904. background: white;
  1905. box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  1906. padding: 40px;
  1907. }
  1908. .docx-container :deep(p) {
  1909. margin: 0 0 12px 0;
  1910. text-align: justify;
  1911. }
  1912. .docx-container :deep(table) {
  1913. width: 100%;
  1914. border-collapse: collapse;
  1915. margin: 16px 0;
  1916. }
  1917. .docx-container :deep(table td),
  1918. .docx-container :deep(table th) {
  1919. border: 1px solid #ddd;
  1920. padding: 8px;
  1921. text-align: left;
  1922. }
  1923. .docx-container :deep(table th) {
  1924. background-color: #f5f5f5;
  1925. font-weight: bold;
  1926. }
  1927. .docx-container :deep(h1),
  1928. .docx-container :deep(h2),
  1929. .docx-container :deep(h3),
  1930. .docx-container :deep(h4),
  1931. .docx-container :deep(h5),
  1932. .docx-container :deep(h6) {
  1933. margin: 20px 0 12px 0;
  1934. font-weight: bold;
  1935. }
  1936. .docx-container :deep(.page-break) {
  1937. page-break-before: always;
  1938. margin-top: 40px;
  1939. padding-top: 40px;
  1940. border-top: 1px dashed #ccc;
  1941. }
  1942. </style>