From 3c738f4fe2762bba8087e5a22fc0dc06560eab0e Mon Sep 17 00:00:00 2001
From: linwe <872216996@qq.com>
Date: Thu, 08 Aug 2024 10:01:17 +0800
Subject: [PATCH] 三色任务与自查任务

---
 subPackage/workbench/views/checkReform.vue |  686 +++++++++++++++++++++++++++------------------------------
 1 files changed, 324 insertions(+), 362 deletions(-)

diff --git a/subPackage/workbench/views/checkReform.vue b/subPackage/workbench/views/checkReform.vue
index ce57058..d7a3c09 100644
--- a/subPackage/workbench/views/checkReform.vue
+++ b/subPackage/workbench/views/checkReform.vue
@@ -1,185 +1,271 @@
 <template>
-	<view class="wrap">
-		<u-collapse :value="itemIndex" ref="collapse">
-			<u-collapse-item :title="item.title" :name="index" v-for="(item,index)  in questionList">
-				<u-form :model="info" ref="form">
-					<block v-for="(i,k) in item.children">
-						<view class="f-30 c-33">
-							{{i.title}}
-						</view>
-						<u-form-item labelWidth="90" label="隐患" prop="gateStatus">
-							<view class="btn-group flex">
-								<view class="value-item" :class="i.state == 0?'active bgc-main c-ff':''"
-									@click="changeStatus(0,i,index,k)">
-									存在
-								</view>
-								<view class="value-item" :class="i.state == 1?'active bgc-main c-ff':''"
-									@click="changeStatus(1,i,index,k)">
-									不存在
-								</view>
-							</view>
-						</u-form-item>
-						<view class="" v-if="i.state == 0">
-							<u-form-item labelWidth="100" label="隐患照片" prop="gateStatus">
-								<u-upload :fileList="i.urls" :previewFullImage="uploadConfig.previewFullImage"
-									:accept="uploadConfig.acceptImg" :multiple="uploadConfig.multiple"
-									:maxCount="uploadConfig.maxCount" :capture="uploadConfig.capture"
-									@afterRead="afterReadImgs($event,i,index,k)" @delete="deleteImages($event,index,k)">
-									<view class="upload-item upload-icon flex_base">
-										<u-icon name="/static/icon/upload.png" width="60rpx" height="60rpx"></u-icon>
-									</view>
-								</u-upload>
-							</u-form-item>
-							<u-form-item labelWidth="100" label="隐患描述" prop="gateStatus">
-								<u-textarea v-model="i.remark" placeholder="请输入隐患描述"
-									placeholderClass="c-99 f-26"></u-textarea>
-							</u-form-item>
-						</view>
-					</block>
-				</u-form>
-			</u-collapse-item>
-		</u-collapse>
+	<view class="">
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>场所名称</text>
+			<text class="item-content">{{info.placeName}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>标签</text>
+			<text>{{label || "未完善"}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>场所负责人</text>
+			<text>{{info.principal || "未完善"}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>场所负责人电话</text>
+			<text>{{info.principalPhone || "未完善"}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>检查人</text>
+			<text>{{info.name}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>检查时间</text>
+			<text>{{info.createTime}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>地址</text>
+			<text class="item-content">{{info.location}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c" v-if="info.rectificationNoticeFlag == 2">
+			<text>下发整改通知</text>
+			<text class="item-content">是</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c" v-if="info.punishFlag == 2">
+			<text>是否处罚</text>
+			<text class="item-content">是</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c" v-if="info.punishRemark">
+			<text>处罚说明</text>
+			<text class="item-content">{{info.punishRemark}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c" v-if="info.rectificationEndTime">
+			<text>整改截至时间</text>
+			<text class="item-content">{{info.rectificationEndTime}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c" v-if="info.remark">
+			<text>其它隐患问题</text>
+			<text class="item-content">{{info.remark}}</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c">
+			<text>状态</text>
+			<text v-if="info.status == 1">待审核</text>
+			<text v-if="info.status == 2">审核成功</text>
+			<text v-if="info.status == 3">审核驳回</text>
+			<text v-if="info.status == 4">待完成</text>
+		</view>
+		<view class="item-row flex j-c-s-b a-i-c" v-if="info.status == 3 && info.reasonFailure">
+			<text>驳回原因</text>
+			<text class="item-content">{{info.reasonFailure}}</text>
+		</view>
 
-		<view class="content">
-			<view class="upload ">
-				<view class="f-28 mb-20">其它隐患问题</view>
-				<u-textarea v-model="info.remark" placeholder="请输入内容" placeholderClass="c-99 f-26"></u-textarea>
-				<view class="mt-20">
-					<view class="f-28 mb-20">图片</view>
-					<u-upload :fileList="form.images" :previewFullImage="uploadConfig.previewFullImage"
-						:accept="uploadConfig.acceptImg" :multiple="uploadConfig.multiple"
-						:maxCount="uploadConfig.maxCount" :capture="uploadConfig.capture" @afterRead="afterReadImg"
-						@delete="deletePic">
-						<view class="upload-item upload-icon flex_base">
-							<u-icon name="/static/icon/upload.png" width="60rpx" height="60rpx"></u-icon>
-						</view>
-					</u-upload>
+		<view class="image-wrap bgc-ff" v-if="images.length">
+			<view class="mb-20">
+				图片
+			</view>
+			<view class="flex flex-wrap">
+				<view class="image-item" v-for="(i,k) in images" @click="previewImage(images,i)">
+					<u-image :src="i" width="100rpx" height="100rpx"></u-image>
 				</view>
 			</view>
 		</view>
 
-		<footer-btn @click="sumitInfo"></footer-btn>
+		<view class="image-wrap bgc-ff" v-if="signatureUrl.length">
+			<view class="mb-20">
+				签名
+			</view>
+			<view class="image-item" v-for="(i,k) in signatureUrl" @click="previewImage(signatureUrl,i)">
+				<u-image :src="i" width="100rpx" height="100rpx"></u-image>
+			</view>
+		</view>
 
+
+
+		<view class="mt-20 bgc-ff">
+			<block v-for="(item,index) in itemList">
+				<block v-for="(i,k) in item.children">
+					<block v-for="(n,m) in info.patrolRecordVOList">
+						<!-- <view class="item-row flex j-c-s-b a-i-c" v-if="i.id == n.itemId && n.state == 1">
+							<text class="item-left">{{i.title}}</text>
+							<text>不存在</text>
+						</view> -->
+						<u-collapse :value="ids" v-if="i.id == n.itemId && n.state == 0" ref="collapse">
+							<u-collapse-item :name="n.itemId" :title="i.title" value="存在" :disabled="true"
+								:clickable="false" :isLink="false">
+								<view class="mb-20" v-if="n.imageUrls">
+									<view class="f-26 mb-10 c-00">隐患照片</view>
+									<view class="flex flex-wrap">
+										<view class="mr-20" v-for="r in ($setImageUrl(n.imageUrls))">
+											<u-image :src="r" width="140rpx" height="140rpx"
+												@click="previewImage($setImageUrl(n.imageUrls),i)"></u-image>
+										</view>
+									</view>
+								</view>
+
+								<view class="mb-20" v-if="n.remark">
+									<view class="f-26 mb-10 c-00">隐患描述</view>
+									<view class="f-28 c-00">
+										{{n.remark}}
+									</view>
+								</view>
+
+								<view class="mb-20" v-if="roleType == 2 || (roleType == 1 && n.rectificationImageUrls)">
+									<view class="f-26 mb-10 c-00">整改后照片</view>
+									<u-upload v-if="roleType == 2 && (info.status == 4 || info.status == 3)"
+										:fileList="n.urls" :previewFullImage="uploadConfig.previewFullImage"
+										:accept="uploadConfig.acceptImg" :multiple="uploadConfig.multiple"
+										:maxCount="uploadConfig.maxCount" :capture="uploadConfig.capture"
+										@afterRead="afterReadImgs($event,n,m)" @delete="deleteImages($event,m)">
+										<view class="upload-item upload-icon flex_base">
+											<u-icon name="/static/icon/upload.png" width="60rpx"
+												height="60rpx"></u-icon>
+										</view>
+									</u-upload>
+
+
+									<view class="flex flex-wrap" v-if="info.status != 4 &&  n.rectificationImageUrls ">
+										<view class="mr-20" v-for="r in ($setImageUrl(n.rectificationImageUrls))">
+											<u-image :src="r" width="140rpx" height="140rpx"
+												@click="previewImage($setImageUrl(n.rectificationImageUrls),i)"></u-image>
+										</view>
+									</view>
+								</view>
+
+								<view class="" v-if="roleType == 2 || (roleType == 1 && n.rectificationRemark)">
+									<view class="f-26 mb-10 c-00">整改后描述</view>
+									<u-textarea v-model="n.rectificationRemark" placeholder="请输入整改后描述"
+										placeholderClass="c-99 f-26"
+										:disabled="roleType == 2 && (info.status == 4 || info.status == 3)?false:true"></u-textarea>
+								</view>
+							</u-collapse-item>
+						</u-collapse>
+					</block>
+				</block>
+			</block>
+		</view>
+
+		<view class="blank"></view>
+		<footer-btn @click="submitInfo" v-if="roleType == 2 && (info.status == 4 || info.status == 3)" />
+		<audit-action v-if="roleType == 1 && info.status == 1" @handle="submitAudit" />
 	</view>
 </template>
 
 <script>
-	import uploadMixin from "@/mixin/uploadMixin";
+	import uploadMixin from "@/mixin/uploadMixinPlace";
 	import {
-		addPlaceCheckTwo,
-		getPlaceCheckItem
-	} from "@/api/place/place.js"
+		getPlaceCheckItem,
+		getPlaceCheckReformDetail,
+		addPlaceCheckReform,
+		auditPlaceCheckReform
+	} from '@/api/place/place.js'
+	import auditAction from '../components/actionBtn.vue'
 	export default {
 		mixins: [uploadMixin],
+		components: {
+			auditAction
+		},
 		data() {
 			return {
-				form: {
-					images: []
-				},
-				info: {
-					remark: "",
-					imageUrls: "",
-					signaturePath: "",
-				},
-
-				desc: "",
-				title: "",
-				curSelectSite: {},
-				showSignature: false,
-				signatureUrl: "",
-				signatureSettings: { //签名设置
-					width: '700', //签名区域的宽
-					height: '800', //签名区域的高
-					lineWidth: 3, //签名时线宽
-					textColor: '#007AFF' //签名文字颜色
-				},
-				statusOption: [{
-						text: "存在",
-						value: 0
-					},
-					{
-						text: "不存在",
-						value: 1
-					},
-				],
-
-				questionList: [],
-				itemIndex: [],
-				patrolRecordVOList: [],
-				count: 0,
-				statusList: [{
-						name: "是",
-						id: 1
-					},
-					{
-						name: "否",
-						id: 2
-					}
-				],
-
+				info: {},
+				images: [],
+				signatureUrl: [],
+				label: "",
+				itemList: [],
+				roleType: '', //1.民警/系统管理员   2.场所负责人
+				ids: []
 			}
 		},
-		onLoad(option) {
-			this.info.houseCode = option.code;
-			this.getItem();
+		async onLoad(option) {
+			let {
+				roleName
+			} = uni.getStorageSync("activeRole")
+			if (roleName == "居民") {
+				this.roleType = 2;
+			}
+			if (roleName == "民警" || roleName == "系统管理员") {
+				this.roleType = 1;
+			}
+
+			await this.getItem();
+			this.getDetail(option.id);
 		},
 
 		methods: {
 
 
-			getItem() {
-				getPlaceCheckItem().then(res => {
+			getDetail(id) {
+				getPlaceCheckReformDetail({
+					taskId: id
+				}).then(res => {
 					let data = res.data;
-					this.questionList = res.data;
-					let arr = []
-					let obj = {
-						itemId: "",
-						state: "",
-						imagesUrl: "",
-						remark: ""
+					// this.info = res.data;
+					this.images = this.$setImageUrl(res.data.imageUrls);
+					this.signatureUrl = this.$setImageUrl(res.data.signaturePath);
+					if (res.data.placePoiLabelVOList.length) {
+						this.label = res.data.placePoiLabelVOList[res.data.placePoiLabelVOList.length - 1]
+							.labelName;
 					}
-					let count = 0;
-					for (let i = 0, ii = data.length; i < ii; i++) {
-						arr.push(i);
-						for (let k = 0, kk = data[i].children.length; k < kk; k++) {
-							// this.info.patrolRecordVOList.push(obj);
-							count += 1;
+					let ids = [];
+					for (let i of data.patrolRecordVOList) {
+						if ((data.status == 4 || data.status == 3) && this.roleType == 2) {
+							if (i.rectificationImageUrls) {
+								i.urls = this.$setImageUrl(i.rectificationImageUrls, 2);
+							} else {
+								i.urls = [];
+							}
+						}
+						if (i.state == 0) {
+							ids.push(i.itemId)
 						}
 					}
-					this.count = count;
-					this.itemIndex = arr;
+					this.ids = ids;
+					this.info = data;
 				})
 			},
 
-			changeStatus(value, item, fIndex, cIndex) {
-				let children = this.questionList[fIndex];
-				children.children[cIndex].state = value;
-				this.$set(this.questionList, fIndex, children)
-				if (value == 0 && !item.urls) {
-					children.children[cIndex].urls = [];
-					this.$set(this.questionList, fIndex, children)
-				}
-				this.$nextTick(() => {
-					this.$refs.collapse.init();
+			getItem() {
+				getPlaceCheckItem({
+					childType: 1,
+					type: 1
+				}).then(res => {
+					if (res.code == 200) {
+						this.itemList = res.data;
+					}
 				})
-				this.getSelected()
 			},
 
-			getSelected() {
-				let arr = []
-				for (let i = 0, ii = this.questionList.length; i < ii; i++) {
-					for (let k = 0, kk = this.questionList[i].children.length; k < kk; k++) {
-						if (this.questionList[i].children[k].state != null) {
-							arr.push({
-								itemId: this.questionList[i].children[k].id,
-								state: this.questionList[i].children[k].state,
-								imageUrls: this.setImages(this.questionList[i].children[k].urls),
-								remark: this.questionList[i].children[k].remark || ""
-							})
-						}
-					}
+			async afterReadImgs(event, item, fIndex) {
+				uni.showLoading({
+					title: "上传中..."
+				})
+				let children = this.info.patrolRecordVOList;
+				let lists = [].concat(event.file)
+				let fileListLen = item.urls.length;
+				lists.map((item) => {
+					children[fIndex].urls.push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+					this.$set(this.info, "patrolRecordVOList", children)
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					children[fIndex].urls.splice(fileListLen, 1, Object.assign({}, {
+						url: result.data.link,
+						name: result.data.name
+					}))
+					this.$set(this.info, "patrolRecordVOList", children)
+					fileListLen++
 				}
-				return arr;
+				this.loadingClose()
+			},
+
+			deleteImages(event, index) {
+				let children = this.info.patrolRecordVOList;
+				children[index].urls.splice(event.index, 1);
+				this.$set(this.info, "patrolRecordVOList", children)
 			},
 
 			setImages(key) {
@@ -194,161 +280,112 @@
 				}
 			},
 
-			async afterReadImgs(event, item, fIndex, cIndex) {
-				this.showLoading()
-				let children = this.questionList[fIndex];
-				let lists = [].concat(event.file)
-				let fileListLen = item.urls.length;
-				lists.map((item) => {
-					children.children[cIndex].urls.push({
-						...item,
-						status: 'uploading',
-						message: '上传中'
-					})
-					this.$set(this.questionList, fIndex, children)
-				})
-				for (let i = 0; i < lists.length; i++) {
-					const result = await this.uploadFilePromise(lists[i].url)
-					children.children[cIndex].urls.splice(fileListLen, 1, Object.assign({}, {
-						url: result.data.link,
-						name: result.data.name
-					}))
-					this.$set(this.questionList, fIndex, children)
-					fileListLen++
-				}
-				this.loadingClose()
-				this.getSelected()
-			},
-
-			deleteImages(event, fIndex, cIndex) {
-				let children = this.questionList[fIndex];
-				children.children[cIndex].urls.splice(event.index, 1);
-				this.$set(this.questionList, fIndex, children);
-				this.getSelected()
-			},
-
-			signatureChange(e) {
-				// this.signatureUrl = e;
-				this.uploadSignImg(e)
-			},
-
-			showSignPopup() {
-				// if (this.getSelected().length < this.count) {
-				// 	this.$showTips("还有内容还未完善");
-				// 	return;
-				// }
-
-				let data = this.getSelected();
-				for (let i of data) {
-					if (i.state == 1) {
-						if (i.imageUrls || i.remark) {
-							i.imageUrls = "";
-							i.remark = "";
-						}
-					} else {
-						if (!i.imageUrls && !i.remark) {
-							this.$showTips("请上传隐患照片或填写隐患描述");
-							return;
-						}
-					}
-				}
-				this.showSignature = true;
-			},
-
-			uploadSignImg(url) {
-				uni.uploadFile({
-					url: this.uploadConfig.url,
-					filePath: url,
-					name: 'file',
-					header: this.uploadConfig.header,
-					success: (res) => {
-						console.log(res);
-						let data = JSON.parse(res.data);
-						if (res.statusCode == 200 && data.code == 200) {
-							// this.handleSubmit(data.data.name);
-							this.showSignature = false;
-							this.signatureUrl = data.data.link;
-							this.info.signaturePath = data.data.name;
-							// this.sumitInfo()
-						}
-					}
+			previewImage(urls, current) {
+				uni.previewImage({
+					urls,
+					current
 				})
 			},
 
-			sumitInfo() {
+			//整改提交
+			submitInfo() {
 				uni.showLoading({
 					title: "加载中..."
 				})
-				if (this.form.images.length > 0) {
-					let urls = []
-					this.form.images.forEach(e => {
-						urls.push(e.name)
-					})
-					this.info.imageUrls = urls.join(",")
+				let data = this.info;
+				for (let i of data.patrolRecordVOList) {
+					if (i.urls) {
+						i.rectificationImageUrls = this.setImages(i.urls)
+					}
+					delete i.urls;
 				}
-
-				this.info.patrolRecordVOList = this.getSelected()
-
-				addPlaceCheckTwo(this.info).then(res => {
-					uni.hideLoading();
-					uni.showToast({
-						icon: 'success',
-						title: '提交成功',
-						success() {
-							setTimeout(() => {
-								uni.navigateBack();
-							}, 1000)
-						}
-					})
+				if (data.status == 3) {
+					data.status = 1;
+				}
+				if (data.status == 4) {
+					data.status = 1;
+				}
+				addPlaceCheckReform(data).then(res => {
+					uni.hideLoading()
+					if (res.code == 200) {
+						this.$showTips("操作成功", "success");
+						setTimeout(() => {
+							uni.navigateBack()
+						}, 300)
+					}
 				})
 			},
 
-			previewImg() {
-				uni.previewImage({
-					urls: [this.signatureUrl],
-					current: this.signatureUrl
+			//审核
+			submitAudit(val) {
+				uni.showLoading({
+					title: "加载中..."
+				})
+				auditPlaceCheckReform({
+					id: this.info.id,
+					taskId: this.info.taskId,
+					status: val.type,
+					rectificationFlag: val.type == 3 ? 1 : 2,
+					reasonFailure: val.remark
+				}).then(res => {
+					uni.hideLoading()
+					if (res.code == 200) {
+						this.$showTips("操作成功", "success");
+						setTimeout(() => {
+							uni.navigateBack()
+						}, 300)
+					}
 				})
 			}
-
 		}
 	}
 </script>
 
-<style lang="less">
-	.wrap {
-		padding: 20rpx;
+<style lang="scss">
+	page {
+		background-color: #f5f5f5;
 	}
 
-	.top {
-		display: flex;
-		justify-content: flex-end;
-		padding: 20rpx;
-	}
-
-	.top-btn {
-		padding: 16rpx 22rpx;
-		border-radius: 6rpx;
-		text-align: center;
-	}
-
-	.content {
-		margin: 20rpx 0;
-		padding: 0 30rpx 150rpx;
-	}
-
-	.site-box {
+	.item-row {
+		width: 100%;
 		padding: 30rpx;
-		margin: 20rpx 30rpx;
-		border-radius: 4rpx;
+		box-sizing: border-box;
+		border-bottom: 1px solid #f5f5f5;
+		font-size: 28rpx;
+		background-color: #fff;
 
-		.c-aa {
-			color: #aaa;
+		.item-content {
+			width: 70%;
+			text-align: right;
 		}
 
-		.address {
-			width: 60%;
-			margin-left: 25rpx;
+		.item-left {
+			width: 70%;
 		}
+	}
+
+	.image-wrap {
+		margin-top: 20rpx;
+		padding: 20rpx;
+
+		.image-item {
+			margin-right: 20rpx;
+		}
+	}
+
+	/deep/.u-cell__title-text {
+		color: #000 !important;
+		width: 70%;
+		font-size: 28rpx !important;
+	}
+
+	/deep/ .u-cell__value {
+		color: #000 !important;
+		font-size: 28rpx !important;
+	}
+
+	.mr-20 {
+		margin-right: 20rpx;
 	}
 
 	.upload-item {
@@ -357,91 +394,16 @@
 		border: 1px solid #EEEEEE;
 	}
 
-	.upload {
-		// margin: 0 30rpx;
-		padding: 30rpx 0;
+	.blank {
+		width: 100%;
+		height: 140rpx;
 	}
 
-	.submit-btn {
-		width: 690rpx;
-		height: 78rpx;
-		line-height: 78rpx;
-		background: linear-gradient(163deg, #01BDFC 0%, #017BFC 100%);
-		border-radius: 8rpx 8rpx 8rpx 8rpx;
-		font-size: 32rpx;
-		color: #fff;
-		margin-top: 50rpx;
+	.c-00 {
+		color: #000;
 	}
 
-	.list-btn {
-		width: 690rpx;
-		height: 78rpx;
-		line-height: 78rpx;
-		background: linear-gradient(163deg, #c7d7dc 0%, #c3cdd8 100%);
-		border-radius: 8rpx 8rpx 8rpx 8rpx;
-		font-size: 32rpx;
-		color: #fff;
-		margin-top: 50rpx;
-	}
-
-	.address-row {
-		flex: 1;
-		justify-content: flex-end;
-		align-items: center;
-	}
-
-	.address-content {
-		width: calc(100% - 116rpx - 20rpx);
-		margin-right: 20rpx;
-		text-align: right;
-	}
-
-	.location-btn {
-		width: 116rpx;
-		height: 46rpx;
-		line-height: 46rpx;
-		border-radius: 4rpx;
-		border: 1px solid currentColor;
-		padding: 0;
-		background-color: #fff;
-		text-align: center;
-	}
-
-
-	.signture-popup {
-		padding-bottom: 20rpx;
-	}
-
-	.sign {
-		padding: 30rpx;
-		// margin: 0 30rpx;
-	}
-
-	.value-item {
-		flex: 1;
-		// background-color: #ffffff;
-		// width: 120rpx;
-		border: 2rpx solid #f5f5f5;
-		height: 70rpx;
-		line-height: 70rpx;
-		box-sizing: border-box;
-		text-align: center;
-	}
-
-	.active {
-		border: 2rpx solid currentColor;
-	}
-
-	.btn-group {
-		flex: 1;
-	}
-
-	/deep/ .u-cell__title-text {
-		font-weight: bold;
-		font-size: 30rpx;
-	}
-
-	/deep/ .u-form-item__body__left__content {
-		font-size: 26rpx;
+	/deep/ .u-collapse-item .u-collapse-item__content {
+		height: auto !important;
 	}
 </style>
\ No newline at end of file

--
Gitblit v1.9.3